From 79b9e07ee97f0dfa12a60125c83af61adb9c38bf Mon Sep 17 00:00:00 2001 From: SciresM Date: Mon, 13 Apr 2020 17:07:37 -0700 Subject: [PATCH] erpt: reimplement the sysmodule (#875) * erpt: reimplement the sysmodule * fatal: update for latest bindings * erpt: amend logic for culling orphan attachments --- .../libstratosphere/include/stratosphere.hpp | 68 ++--- .../include/stratosphere/erpt.hpp | 6 + .../erpt/erpt_multiple_category_context.hpp | 35 +++ .../include/stratosphere/erpt/erpt_types.hpp | 234 ++++++++++++++++++ .../erpt/sf/erpt_sf_i_attachment.hpp | 51 ++++ .../erpt/sf/erpt_sf_i_context.hpp | 69 ++++++ .../erpt/sf/erpt_sf_i_manager.hpp | 51 ++++ .../stratosphere/erpt/sf/erpt_sf_i_report.hpp | 51 ++++ .../erpt/sf/erpt_sf_i_session.hpp | 45 ++++ .../stratosphere/erpt/srv/erpt_srv_api.hpp | 33 +++ .../stratosphere/erpt/srv/erpt_srv_types.hpp | 86 +++++++ .../fs/fs_save_data_management.hpp | 5 + .../include/stratosphere/fs/fs_sd_card.hpp | 2 + .../fssystem_path_resolution_filesystem.hpp | 11 +- .../mem/mem_standard_allocator.hpp | 2 +- .../include/stratosphere/psc.hpp | 22 ++ .../stratosphere/psc/psc_pm_module.hpp | 24 ++ .../psc/psc_pm_module.os.horizon.hpp | 49 ++++ .../stratosphere/psc/psc_pm_module_id.hpp | 83 +++++++ .../include/stratosphere/psc/psc_types.hpp | 39 +++ .../psc/sf/psc_sf_i_pm_module.hpp | 50 ++++ .../psc/sf/psc_sf_i_pm_service.hpp | 37 +++ .../include/stratosphere/settings.hpp | 4 + .../system/settings_firmware_version.hpp | 68 +++++ .../system/settings_product_model.hpp | 30 +++ .../settings/system/settings_region.hpp | 34 +++ .../system/settings_serial_number.hpp | 29 +++ .../include/stratosphere/time.hpp | 26 ++ .../time/impl/util/time_impl_util_api.hpp | 26 ++ .../include/stratosphere/time/time_api.hpp | 34 +++ .../include/stratosphere/time/time_common.hpp | 23 ++ .../stratosphere/time/time_posix_time.hpp | 42 ++++ .../time_standard_network_system_clock.hpp | 40 +++ .../time/time_standard_steady_clock.hpp | 39 +++ .../time/time_standard_user_system_clock.hpp | 40 +++ .../time/time_steady_clock_time_point.hpp | 36 +++ .../time/time_system_clock_common.hpp | 30 +++ .../source/crypto/crypto_csrng.os.horizon.cpp | 47 ++++ .../source/erpt/srv/erpt_srv_allocator.hpp | 50 ++++ .../source/erpt/srv/erpt_srv_attachment.cpp | 84 +++++++ .../source/erpt/srv/erpt_srv_attachment.hpp | 62 +++++ .../erpt/srv/erpt_srv_attachment_impl.cpp | 79 ++++++ .../erpt/srv/erpt_srv_attachment_impl.hpp | 38 +++ .../source/erpt/srv/erpt_srv_cipher.cpp | 24 ++ .../source/erpt/srv/erpt_srv_cipher.hpp | 125 ++++++++++ .../source/erpt/srv/erpt_srv_context.cpp | 131 ++++++++++ .../source/erpt/srv/erpt_srv_context.hpp | 45 ++++ .../source/erpt/srv/erpt_srv_context_impl.cpp | 155 ++++++++++++ .../source/erpt/srv/erpt_srv_context_impl.hpp | 36 +++ .../erpt/srv/erpt_srv_context_record.cpp | 201 +++++++++++++++ .../erpt/srv/erpt_srv_context_record.hpp | 49 ++++ .../source/erpt/srv/erpt_srv_formatter.hpp | 185 ++++++++++++++ .../source/erpt/srv/erpt_srv_journal.cpp | 119 +++++++++ .../source/erpt/srv/erpt_srv_journal.hpp | 119 +++++++++ .../srv/erpt_srv_journal_for_attachments.cpp | 219 ++++++++++++++++ .../erpt/srv/erpt_srv_journal_for_meta.cpp | 71 ++++++ .../erpt/srv/erpt_srv_journal_for_reports.cpp | 225 +++++++++++++++++ .../erpt/srv/erpt_srv_journal_record.hpp | 36 +++ .../source/erpt/srv/erpt_srv_keys.cpp | 98 ++++++++ .../source/erpt/srv/erpt_srv_keys.hpp | 27 ++ .../source/erpt/srv/erpt_srv_main.cpp | 132 ++++++++++ .../source/erpt/srv/erpt_srv_manager_impl.cpp | 95 +++++++ .../source/erpt/srv/erpt_srv_manager_impl.hpp | 40 +++ .../source/erpt/srv/erpt_srv_ref_count.hpp | 40 +++ .../source/erpt/srv/erpt_srv_report.cpp | 90 +++++++ .../source/erpt/srv/erpt_srv_report.hpp | 63 +++++ .../source/erpt/srv/erpt_srv_report_impl.cpp | 79 ++++++ .../source/erpt/srv/erpt_srv_report_impl.hpp | 38 +++ .../source/erpt/srv/erpt_srv_reporter.cpp | 226 +++++++++++++++++ .../source/erpt/srv/erpt_srv_reporter.hpp | 83 +++++++ .../source/erpt/srv/erpt_srv_service.cpp | 134 ++++++++++ .../source/erpt/srv/erpt_srv_service.hpp | 24 ++ .../source/erpt/srv/erpt_srv_session_impl.cpp | 54 ++++ .../source/erpt/srv/erpt_srv_session_impl.hpp | 28 +++ .../source/erpt/srv/erpt_srv_stream.cpp | 188 ++++++++++++++ .../source/erpt/srv/erpt_srv_stream.hpp | 59 +++++ .../source/fs/fs_save_data_management.cpp | 20 ++ .../libstratosphere/source/fs/fs_sd_card.cpp | 25 ++ .../nim/nim_network_install_manager_api.cpp | 2 +- .../source/psc/psc_pm_module.os.horizon.cpp | 74 ++++++ .../source/psc/psc_remote_pm_module.hpp | 58 +++++ .../impl/settings_firmware_version_impl.cpp | 26 ++ .../impl/settings_firmware_version_impl.hpp | 23 ++ .../impl/settings_product_model_impl.cpp | 25 ++ .../impl/settings_product_model_impl.hpp | 23 ++ .../settings/impl/settings_region_impl.cpp | 26 ++ .../settings/impl/settings_region_impl.hpp | 23 ++ .../impl/settings_serial_number_impl.cpp | 26 ++ .../impl/settings_serial_number_impl.hpp | 23 ++ .../settings/settings_firmware_version.cpp | 25 ++ .../settings/settings_product_model.cpp | 27 ++ .../source/settings/settings_region.cpp | 28 +++ .../settings/settings_serial_number.cpp | 25 ++ .../time/impl/util/time_impl_util_api.cpp | 34 +++ .../libstratosphere/source/time/time_api.cpp | 105 ++++++++ .../time_standard_network_system_clock.cpp | 44 ++++ .../time/time_standard_steady_clock.cpp | 46 ++++ .../time/time_standard_user_system_clock.cpp | 44 ++++ .../libvapours/include/vapours/crypto.hpp | 2 + .../include/vapours/crypto/crypto_csrng.hpp | 26 ++ .../crypto/crypto_rsa_oaep_decryptor.hpp | 7 +- .../crypto/crypto_rsa_oaep_encryptor.hpp | 137 ++++++++++ .../crypto_rsa_oaep_sha256_encryptor.hpp | 53 ++++ .../crypto/impl/crypto_rsa_oaep_impl.hpp | 33 +++ .../libvapours/include/vapours/results.hpp | 3 + .../include/vapours/results/erpt_results.hpp | 43 ++++ .../include/vapours/results/psc_results.hpp | 27 ++ .../include/vapours/results/time_results.hpp | 32 +++ libraries/libvapours/include/vapours/util.hpp | 1 + .../include/vapours/util/util_bitflagset.hpp | 6 +- .../include/vapours/util/util_string_util.hpp | 43 ++++ .../include/vapours/util/util_uuid.hpp | 54 +++- stratosphere/erpt/Makefile | 128 ++++++++++ stratosphere/erpt/erpt.json | 88 +++++++ stratosphere/erpt/source/erpt_main.cpp | 176 +++++++++++++ stratosphere/fatal/source/fatal_config.cpp | 4 +- stratosphere/fatal/source/fatal_config.hpp | 8 +- 117 files changed, 6716 insertions(+), 59 deletions(-) create mode 100644 libraries/libstratosphere/include/stratosphere/erpt/erpt_multiple_category_context.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/erpt/erpt_types.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_attachment.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_context.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_manager.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_report.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_session.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/erpt/srv/erpt_srv_api.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/erpt/srv/erpt_srv_types.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/psc.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/psc/psc_pm_module.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/psc/psc_pm_module.os.horizon.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/psc/psc_pm_module_id.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/psc/psc_types.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/psc/sf/psc_sf_i_pm_module.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/psc/sf/psc_sf_i_pm_service.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/settings/system/settings_firmware_version.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/settings/system/settings_product_model.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/settings/system/settings_region.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/settings/system/settings_serial_number.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/time.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/time/impl/util/time_impl_util_api.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/time/time_api.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/time/time_common.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/time/time_posix_time.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/time/time_standard_network_system_clock.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/time/time_standard_steady_clock.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/time/time_standard_user_system_clock.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/time/time_steady_clock_time_point.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/time/time_system_clock_common.hpp create mode 100644 libraries/libstratosphere/source/crypto/crypto_csrng.os.horizon.cpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_allocator.hpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_attachment.cpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_attachment.hpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_attachment_impl.cpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_attachment_impl.hpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_cipher.cpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_cipher.hpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_context.cpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_context.hpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_context_impl.cpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_context_impl.hpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_context_record.cpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_context_record.hpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_formatter.hpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_journal.cpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_journal.hpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_journal_for_attachments.cpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_journal_for_meta.cpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_journal_for_reports.cpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_journal_record.hpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_keys.cpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_keys.hpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_main.cpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_manager_impl.cpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_manager_impl.hpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_ref_count.hpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_report.cpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_report.hpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_report_impl.cpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_report_impl.hpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.cpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.hpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_service.cpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_service.hpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_session_impl.cpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_session_impl.hpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_stream.cpp create mode 100644 libraries/libstratosphere/source/erpt/srv/erpt_srv_stream.hpp create mode 100644 libraries/libstratosphere/source/psc/psc_pm_module.os.horizon.cpp create mode 100644 libraries/libstratosphere/source/psc/psc_remote_pm_module.hpp create mode 100644 libraries/libstratosphere/source/settings/impl/settings_firmware_version_impl.cpp create mode 100644 libraries/libstratosphere/source/settings/impl/settings_firmware_version_impl.hpp create mode 100644 libraries/libstratosphere/source/settings/impl/settings_product_model_impl.cpp create mode 100644 libraries/libstratosphere/source/settings/impl/settings_product_model_impl.hpp create mode 100644 libraries/libstratosphere/source/settings/impl/settings_region_impl.cpp create mode 100644 libraries/libstratosphere/source/settings/impl/settings_region_impl.hpp create mode 100644 libraries/libstratosphere/source/settings/impl/settings_serial_number_impl.cpp create mode 100644 libraries/libstratosphere/source/settings/impl/settings_serial_number_impl.hpp create mode 100644 libraries/libstratosphere/source/settings/settings_firmware_version.cpp create mode 100644 libraries/libstratosphere/source/settings/settings_product_model.cpp create mode 100644 libraries/libstratosphere/source/settings/settings_region.cpp create mode 100644 libraries/libstratosphere/source/settings/settings_serial_number.cpp create mode 100644 libraries/libstratosphere/source/time/impl/util/time_impl_util_api.cpp create mode 100644 libraries/libstratosphere/source/time/time_api.cpp create mode 100644 libraries/libstratosphere/source/time/time_standard_network_system_clock.cpp create mode 100644 libraries/libstratosphere/source/time/time_standard_steady_clock.cpp create mode 100644 libraries/libstratosphere/source/time/time_standard_user_system_clock.cpp create mode 100644 libraries/libvapours/include/vapours/crypto/crypto_csrng.hpp create mode 100644 libraries/libvapours/include/vapours/crypto/crypto_rsa_oaep_encryptor.hpp create mode 100644 libraries/libvapours/include/vapours/crypto/crypto_rsa_oaep_sha256_encryptor.hpp create mode 100644 libraries/libvapours/include/vapours/results/erpt_results.hpp create mode 100644 libraries/libvapours/include/vapours/results/psc_results.hpp create mode 100644 libraries/libvapours/include/vapours/results/time_results.hpp create mode 100644 libraries/libvapours/include/vapours/util/util_string_util.hpp create mode 100644 stratosphere/erpt/Makefile create mode 100644 stratosphere/erpt/erpt.json create mode 100644 stratosphere/erpt/source/erpt_main.cpp diff --git a/libraries/libstratosphere/include/stratosphere.hpp b/libraries/libstratosphere/include/stratosphere.hpp index 58e3d8355..31c66bad1 100644 --- a/libraries/libstratosphere/include/stratosphere.hpp +++ b/libraries/libstratosphere/include/stratosphere.hpp @@ -20,47 +20,49 @@ #include /* Libstratosphere-only utility. */ -#include "stratosphere/util.hpp" +#include /* Sadly required shims. */ -#include "stratosphere/svc/svc_stratosphere_shims.hpp" +#include /* Critical modules with no dependencies. */ -#include "stratosphere/ams.hpp" -#include "stratosphere/os.hpp" -#include "stratosphere/dd.hpp" -#include "stratosphere/lmem.hpp" -#include "stratosphere/mem.hpp" +#include +#include +#include +#include +#include /* Pull in all ID definitions from NCM. */ -#include "stratosphere/ncm/ncm_ids.hpp" +#include /* At this point, just include the rest alphabetically. */ /* TODO: Figure out optimal order. */ -#include "stratosphere/boot2.hpp" -#include "stratosphere/cfg.hpp" -#include "stratosphere/dmnt.hpp" -#include "stratosphere/erpt.hpp" -#include "stratosphere/fatal.hpp" -#include "stratosphere/hid.hpp" -#include "stratosphere/hos.hpp" -#include "stratosphere/kvdb.hpp" -#include "stratosphere/ldr.hpp" -#include "stratosphere/lr.hpp" -#include "stratosphere/map.hpp" -#include "stratosphere/ncm.hpp" -#include "stratosphere/nim.hpp" -#include "stratosphere/patcher.hpp" -#include "stratosphere/pm.hpp" -#include "stratosphere/reg.hpp" -#include "stratosphere/ro.hpp" -#include "stratosphere/settings.hpp" -#include "stratosphere/sf.hpp" -#include "stratosphere/sm.hpp" -#include "stratosphere/spl.hpp" -#include "stratosphere/updater.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* Include FS last. */ -#include "stratosphere/fs.hpp" -#include "stratosphere/fssrv.hpp" -#include "stratosphere/fssystem.hpp" \ No newline at end of file +#include +#include +#include \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/erpt.hpp b/libraries/libstratosphere/include/stratosphere/erpt.hpp index 0ac5614df..e4020d5f1 100644 --- a/libraries/libstratosphere/include/stratosphere/erpt.hpp +++ b/libraries/libstratosphere/include/stratosphere/erpt.hpp @@ -17,3 +17,9 @@ #pragma once #include +#include +#include +#include +#include +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/erpt/erpt_multiple_category_context.hpp b/libraries/libstratosphere/include/stratosphere/erpt/erpt_multiple_category_context.hpp new file mode 100644 index 000000000..7999d13da --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/erpt/erpt_multiple_category_context.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +namespace ams::erpt { + + constexpr inline u32 CategoriesPerMultipleCategoryContext = 0x10; + constexpr inline u32 FieldsPerMultipleCategoryContext = CategoriesPerMultipleCategoryContext * 4; + + struct MultipleCategoryContextEntry : public sf::LargeData, public sf::PrefersMapAliasTransferMode { + u32 version; + u32 category_count; + CategoryId categories[CategoriesPerMultipleCategoryContext]; + u32 field_counts[CategoriesPerMultipleCategoryContext]; + u32 array_buf_counts[CategoriesPerMultipleCategoryContext]; + FieldEntry fields[FieldsPerMultipleCategoryContext]; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/erpt/erpt_types.hpp b/libraries/libstratosphere/include/stratosphere/erpt/erpt_types.hpp new file mode 100644 index 000000000..bc29605e2 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/erpt/erpt_types.hpp @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include + +namespace ams::erpt { + + #define GENERATE_ENUM(NAME, ID, ...) NAME = ID, + + enum FieldType { + AMS_ERPT_FOREACH_FIELD_TYPE(GENERATE_ENUM) + FieldType_Count, + }; + + #undef GENERATE_ENUM + + #define GENERATE_ENUM(NAME, ID, ...) CategoryId_##NAME = ID, + + enum CategoryId { + AMS_ERPT_FOREACH_CATEGORY(GENERATE_ENUM) + CategoryId_Count, + }; + + #undef GENERATE_ENUM + + #define GENERATE_ENUM(NAME, ID, ...) FieldId_##NAME = ID, + + enum FieldId { + AMS_ERPT_FOREACH_FIELD(GENERATE_ENUM) + FieldId_Count, + }; + + #undef GENERATE_ENUM + + constexpr inline u32 ArrayBufferSizeDefault = 0x100; + constexpr inline u32 ArrayBufferSizeMax = 96_KB; + constexpr inline u32 ArrayFieldSizeMax = 16_KB - 1; + + enum ReportType { + ReportType_Start = 0, + + ReportType_Visible = ReportType_Start, + ReportType_Invisible = 1, + + ReportType_End = 2, + + ReportType_Count = ReportType_End, + + ReportType_Any = ReportType_Count, + }; + + constexpr inline u32 ReportCountMax = 50; + constexpr inline u32 AttachmentsPerReportMax = 5; + constexpr inline u32 AttachmentCountMax = ReportCountMax * AttachmentsPerReportMax; + + constexpr inline u32 ReportMetaDataSize = 0x20; + struct ReportMetaData { + u8 user_data[ReportMetaDataSize]; + }; + + + constexpr inline u32 ReportIdSize = 20; + struct ReportId { + union { + u8 id[ReportIdSize]; + util::Uuid uuid; + #pragma pack(push, 1) + struct { + u32 time_low; + u16 time_mid; + u16 time_high_and_version; + u8 clock_high; + u8 clock_low; + u64 node; + } uuid_data; + #pragma pack(pop) + }; + }; + static_assert(sizeof(ReportId) == ReportIdSize); + + inline bool operator==(const ReportId &lhs, const ReportId &rhs) { + return std::memcmp(lhs.id, rhs.id, sizeof(lhs.uuid)) == 0; + } + + inline bool operator!=(const ReportId &lhs, const ReportId &rhs) { + return !(lhs == rhs); + } + + struct ReportFlag { + using Transmitted = util::BitFlagSet::Flag<0>; + using HasAttachment = util::BitFlagSet::Flag<1>; + }; + + using ReportFlagSet = util::BitFlagSet; + static_assert(std::is_pod::value); + static_assert(sizeof(ReportFlagSet) == sizeof(u32)); + + struct ReportInfo { + ReportType type; + ReportId id; + ReportMetaData meta_data; + ReportFlagSet flags; + time::PosixTime timestamp_user; + time::PosixTime timestamp_network; + s64 report_size; + u64 reserved[3]; + }; + + struct ReportList { + u32 report_count; + ReportInfo reports[ReportCountMax]; + }; + + constexpr inline u32 AttachmentIdSize = 20; + struct AttachmentId { + union { + u8 id[AttachmentIdSize]; + util::Uuid uuid; + }; + }; + static_assert(sizeof(AttachmentId) == AttachmentIdSize); + + inline bool operator==(const AttachmentId &lhs, const AttachmentId &rhs) { + return std::memcmp(lhs.id, rhs.id, sizeof(lhs.uuid)) == 0; + } + + inline bool operator!=(const AttachmentId &lhs, const AttachmentId &rhs) { + return !(lhs == rhs); + } + + struct AttachmentFlag { + using HasOwner = util::BitFlagSet::Flag<1>; + }; + + using AttachmentFlagSet = util::BitFlagSet; + static_assert(std::is_pod::value); + static_assert(sizeof(AttachmentFlagSet) == sizeof(u32)); + + constexpr inline u32 AttachmentNameSizeMax = 0x20; + struct AttachmentInfo { + ReportId owner_report_id; + AttachmentId attachment_id; + AttachmentFlagSet flags; + s64 attachment_size; + char attachment_name[AttachmentNameSizeMax]; + }; + + struct AttachmentList { + u32 attachment_count; + AttachmentInfo attachments[AttachmentsPerReportMax]; + }; + + constexpr inline u32 AttachmentSizeMax = 512_KB; + + struct FieldEntry { + FieldId id; + FieldType type; + union { + u64 value_u64; + u32 value_u32; + u16 value_u16; + u8 value_u8; + s64 value_i64; + s32 value_i32; + s16 value_i16; + s8 value_i8; + bool value_bool; + struct { + u32 start_idx; + u32 size; + } value_array; + }; + }; + + constexpr inline u32 FieldsPerContext = 20; + struct ContextEntry { + u32 version; + u32 field_count; + CategoryId category; + FieldEntry fields[FieldsPerContext]; + u8 *array_buffer; + u32 array_free_count; + u32 array_buffer_size; + }; + + struct StorageUsageStatistics { + util::Uuid journal_uuid; + u32 used_storage_size; + s64 max_report_size; + u32 report_count[ReportType_Count]; + u32 transmitted_count[ReportType_Count]; + u32 untransmitted_count[ReportType_Count]; + }; + + /* https://github.com/msgpack/msgpack/blob/master/spec.md#overview */ + enum class ValueTypeTag { + FixMap = 0x80, + FixArray = 0x90, + FixStr = 0xA0, + False = 0xC2, + True = 0xC3, + Bin8 = 0xC4, + Bin16 = 0xC5, + U8 = 0xCC, + U16 = 0xCD, + U32 = 0xCE, + U64 = 0xCF, + I8 = 0xD0, + I16 = 0xD1, + I32 = 0xD2, + I64 = 0xD3, + Str8 = 0xD9, + Str16 = 0xDA, + Array16 = 0xDC, + Map16 = 0xDE, + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_attachment.hpp b/libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_attachment.hpp new file mode 100644 index 000000000..cf403c4b6 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_attachment.hpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2019-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::erpt::sf { + + class IAttachment : public ams::sf::IServiceObject { + protected: + enum class CommandId { + Open = 0, + Read = 1, + SetFlags = 2, + GetFlags = 3, + Close = 4, + GetSize = 5, + }; + public: + /* Actual commands. */ + virtual Result Open(const AttachmentId &attachment_id) = 0; + virtual Result Read(ams::sf::Out out_count, const ams::sf::OutBuffer &out_buffer) = 0; + virtual Result SetFlags(AttachmentFlagSet flags) = 0; + virtual Result GetFlags(ams::sf::Out out) = 0; + virtual Result Close() = 0; + virtual Result GetSize(ams::sf::Out out) = 0; + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MAKE_SERVICE_COMMAND_META(Open), + MAKE_SERVICE_COMMAND_META(Read), + MAKE_SERVICE_COMMAND_META(SetFlags), + MAKE_SERVICE_COMMAND_META(GetFlags), + MAKE_SERVICE_COMMAND_META(Close), + MAKE_SERVICE_COMMAND_META(GetSize), + }; + }; + +} \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_context.hpp b/libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_context.hpp new file mode 100644 index 000000000..eaf916560 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_context.hpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2019-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include +#include + +namespace ams::erpt::sf { + + class IContext : public ams::sf::IServiceObject { + protected: + enum class CommandId { + SubmitContext = 0, + CreateReport = 1, + SetInitialLaunchSettingsCompletionTime = 2, + ClearInitialLaunchSettingsCompletionTime = 3, + UpdatePowerOnTime = 4, + UpdateAwakeTime = 5, + SubmitMultipleCategoryContext = 6, + UpdateApplicationLaunchTime = 7, + ClearApplicationLaunchTime = 8, + SubmitAttachment = 9, + CreateReportWithAttachments = 10, + }; + public: + /* Actual commands. */ + virtual Result SubmitContext(const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &str_buffer) = 0; + virtual Result CreateReport(ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &str_buffer, const ams::sf::InBuffer &meta_buffer) = 0; + virtual Result SetInitialLaunchSettingsCompletionTime(const time::SteadyClockTimePoint &time_point) = 0; + virtual Result ClearInitialLaunchSettingsCompletionTime() = 0; + virtual Result UpdatePowerOnTime() = 0; + virtual Result UpdateAwakeTime() = 0; + virtual Result SubmitMultipleCategoryContext(const MultipleCategoryContextEntry &ctx_entry, const ams::sf::InBuffer &str_buffer) = 0; + virtual Result UpdateApplicationLaunchTime() = 0; + virtual Result ClearApplicationLaunchTime() = 0; + virtual Result SubmitAttachment(ams::sf::Out out, const ams::sf::InBuffer &attachment_name, const ams::sf::InBuffer &attachment_data) = 0; + virtual Result CreateReportWithAttachments(ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &str_buffer, const ams::sf::InBuffer &attachment_ids_buffer) = 0; + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MAKE_SERVICE_COMMAND_META(SubmitContext), + MAKE_SERVICE_COMMAND_META(CreateReport), + MAKE_SERVICE_COMMAND_META(SetInitialLaunchSettingsCompletionTime, hos::Version_300), + MAKE_SERVICE_COMMAND_META(ClearInitialLaunchSettingsCompletionTime, hos::Version_300), + MAKE_SERVICE_COMMAND_META(UpdatePowerOnTime, hos::Version_300), + MAKE_SERVICE_COMMAND_META(UpdateAwakeTime, hos::Version_300), + MAKE_SERVICE_COMMAND_META(SubmitMultipleCategoryContext, hos::Version_500), + MAKE_SERVICE_COMMAND_META(UpdateApplicationLaunchTime, hos::Version_600), + MAKE_SERVICE_COMMAND_META(ClearApplicationLaunchTime, hos::Version_600), + MAKE_SERVICE_COMMAND_META(SubmitAttachment, hos::Version_800), + MAKE_SERVICE_COMMAND_META(CreateReportWithAttachments, hos::Version_800), + }; + }; + +} \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_manager.hpp b/libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_manager.hpp new file mode 100644 index 000000000..02998d56a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_manager.hpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2019-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::erpt::sf { + + class IManager : public ams::sf::IServiceObject { + protected: + enum class CommandId { + GetReportList = 0, + GetEvent = 1, + CleanupReports = 2, + DeleteReport = 3, + GetStorageUsageStatistics = 4, + GetAttachmentList = 5, + }; + public: + /* Actual commands. */ + virtual Result GetReportList(const ams::sf::OutBuffer &out_list, ReportType type_filter) = 0; + virtual Result GetEvent(ams::sf::OutCopyHandle out) = 0; + virtual Result CleanupReports() = 0; + virtual Result DeleteReport(const ReportId &report_id) = 0; + virtual Result GetStorageUsageStatistics(ams::sf::Out out) = 0; + virtual Result GetAttachmentList(const ams::sf::OutBuffer &out_buf, const ReportId &report_id) = 0; + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MAKE_SERVICE_COMMAND_META(GetReportList), + MAKE_SERVICE_COMMAND_META(GetEvent), + MAKE_SERVICE_COMMAND_META(CleanupReports, hos::Version_400), + MAKE_SERVICE_COMMAND_META(DeleteReport, hos::Version_500), + MAKE_SERVICE_COMMAND_META(GetStorageUsageStatistics, hos::Version_500), + MAKE_SERVICE_COMMAND_META(GetAttachmentList, hos::Version_800), + }; + }; + +} \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_report.hpp b/libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_report.hpp new file mode 100644 index 000000000..cc053ad84 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_report.hpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2019-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::erpt::sf { + + class IReport : public ams::sf::IServiceObject { + protected: + enum class CommandId { + Open = 0, + Read = 1, + SetFlags = 2, + GetFlags = 3, + Close = 4, + GetSize = 5, + }; + public: + /* Actual commands. */ + virtual Result Open(const ReportId &report_id) = 0; + virtual Result Read(ams::sf::Out out_count, const ams::sf::OutBuffer &out_buffer) = 0; + virtual Result SetFlags(ReportFlagSet flags) = 0; + virtual Result GetFlags(ams::sf::Out out) = 0; + virtual Result Close() = 0; + virtual Result GetSize(ams::sf::Out out) = 0; + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MAKE_SERVICE_COMMAND_META(Open), + MAKE_SERVICE_COMMAND_META(Read), + MAKE_SERVICE_COMMAND_META(SetFlags), + MAKE_SERVICE_COMMAND_META(GetFlags), + MAKE_SERVICE_COMMAND_META(Close), + MAKE_SERVICE_COMMAND_META(GetSize), + }; + }; + +} \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_session.hpp b/libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_session.hpp new file mode 100644 index 000000000..f00aa0fa9 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_session.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include +#include + +namespace ams::erpt::sf { + + class ISession : public ams::sf::IServiceObject { + protected: + enum class CommandId { + OpenReport = 0, + OpenManager = 1, + OpenAttachment = 2, + }; + public: + /* Actual commands. */ + virtual Result OpenReport(ams::sf::Out> out) = 0; + virtual Result OpenManager(ams::sf::Out> out) = 0; + virtual Result OpenAttachment(ams::sf::Out> out) = 0; + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MAKE_SERVICE_COMMAND_META(OpenReport), + MAKE_SERVICE_COMMAND_META(OpenManager), + MAKE_SERVICE_COMMAND_META(OpenAttachment, hos::Version_800), + }; + }; + +} \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/erpt/srv/erpt_srv_api.hpp b/libraries/libstratosphere/include/stratosphere/erpt/srv/erpt_srv_api.hpp new file mode 100644 index 000000000..16c3820d1 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/erpt/srv/erpt_srv_api.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::erpt::srv { + + Result Initialize(u8 *mem, size_t mem_size); + Result InitializeAndStartService(); + + Result SetSerialNumberAndOsVersion(const char *sn, u32 sn_len, const char *os, u32 os_len, const char *os_priv, u32 os_priv_len); + Result SetProductModel(const char *model, u32 model_len); + Result SetRegionSetting(const char *region, u32 region_len); + + /* Atmosphere extension. */ + Result SetRedirectNewReportsToSdCard(bool redirect); + + void Wait(); + +} \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/erpt/srv/erpt_srv_types.hpp b/libraries/libstratosphere/include/stratosphere/erpt/srv/erpt_srv_types.hpp new file mode 100644 index 000000000..b5eaa84e2 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/erpt/srv/erpt_srv_types.hpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +namespace ams::erpt::srv { + + constexpr inline const char ReportOnSdStoragePath[] = "ersd"; + + constexpr inline const char ReportStoragePath[] = "save"; + constexpr inline const char JournalFileName[] = "save:/journal"; + + constexpr size_t ReportFileNameLength = 64; + constexpr size_t AttachmentFileNameLength = 64; + constexpr size_t MaxFieldStringSize = 64; + + struct ReportFileName { + char name[ReportFileNameLength]; + }; + + struct AttachmentFileName { + char name[AttachmentFileNameLength]; + }; + + enum FieldFlag : u8 { + FieldFlag_None = 0, + FieldFlag_Encrypt = 1, + }; + + #define STRINGIZE_HANDLER(NAME, ...) #NAME, + constexpr inline const char * const FieldString[] = { + AMS_ERPT_FOREACH_FIELD(STRINGIZE_HANDLER) + }; + + constexpr inline const char * const CategoryString[] = { + AMS_ERPT_FOREACH_CATEGORY(STRINGIZE_HANDLER) + }; + + constexpr inline const char * const TypeString[] = { + AMS_ERPT_FOREACH_FIELD_TYPE(STRINGIZE_HANDLER) + }; + #undef STRINGIZE_HANDLER + + #define GET_FIELD_CATEGORY(FIELD, ID, CATEGORY, TYPE, FLAG) [FieldId_##FIELD] = CategoryId_##CATEGORY, + constexpr inline const CategoryId FieldToCategoryMap[] = { + AMS_ERPT_FOREACH_FIELD(GET_FIELD_CATEGORY) + }; + #undef GET_FIELD_CATEGORY + + #define GET_FIELD_TYPE(FIELD, ID, CATEGORY, TYPE, FLAG) [FieldId_##FIELD] = TYPE, + constexpr inline const FieldType FieldToTypeMap[] = { + AMS_ERPT_FOREACH_FIELD(GET_FIELD_TYPE) + }; + #undef GET_FIELD_TYPE + + #define GET_FIELD_FLAG(FIELD, ID, CATEGORY, TYPE, FLAG) [FieldId_##FIELD] = FLAG, + constexpr inline const FieldFlag FieldToFlagMap[] = { + AMS_ERPT_FOREACH_FIELD(GET_FIELD_FLAG) + }; + #undef GET_FIELD_FLAG + + constexpr inline ReportFlagSet MakeNoReportFlags() { + return util::MakeBitFlagSet<32, ReportFlag>(); + } + + constexpr inline AttachmentFlagSet MakeNoAttachmentFlags() { + return util::MakeBitFlagSet<32, AttachmentFlag>(); + } + + +} \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_management.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_management.hpp index a6dbbd8ce..c502631f5 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_management.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_save_data_management.hpp @@ -26,4 +26,9 @@ namespace ams::fs { Result GetSaveDataFlags(u32 *out, SaveDataSpaceId space_id, SaveDataId id); Result SetSaveDataFlags(SaveDataId id, SaveDataSpaceId space_id, u32 flags); + Result GetSaveDataAvailableSize(s64 *out, SaveDataId id); + Result GetSaveDataJournalSize(s64 *out, SaveDataId id); + + Result ExtendSaveData(SaveDataSpaceId space_id, SaveDataId id, s64 available_size, s64 journal_size); + } diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_sd_card.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_sd_card.hpp index 981620719..e27c12195 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_sd_card.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_sd_card.hpp @@ -20,4 +20,6 @@ namespace ams::fs { Result MountSdCard(const char *name); + Result MountSdCardErrorReportDirectoryForAtmosphere(const char *name); + } diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/impl/fssystem_path_resolution_filesystem.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/impl/fssystem_path_resolution_filesystem.hpp index b5c00d20e..d07739349 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/impl/fssystem_path_resolution_filesystem.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/impl/fssystem_path_resolution_filesystem.hpp @@ -14,15 +14,16 @@ * along with this program. If not, see . */ #pragma once -#include "../../fs/fs_common.hpp" -#include "../../fs/fsa/fs_ifile.hpp" -#include "../../fs/fsa/fs_idirectory.hpp" -#include "../../fs/fsa/fs_ifilesystem.hpp" +#include +#include +#include +#include +#include namespace ams::fssystem::impl { template - class IPathResolutionFileSystem : public fs::fsa::IFileSystem { + class IPathResolutionFileSystem : public fs::fsa::IFileSystem, public fs::impl::Newable { NON_COPYABLE(IPathResolutionFileSystem); private: std::shared_ptr shared_fs; diff --git a/libraries/libstratosphere/include/stratosphere/mem/mem_standard_allocator.hpp b/libraries/libstratosphere/include/stratosphere/mem/mem_standard_allocator.hpp index 4a265c4ca..cf8f7fe08 100644 --- a/libraries/libstratosphere/include/stratosphere/mem/mem_standard_allocator.hpp +++ b/libraries/libstratosphere/include/stratosphere/mem/mem_standard_allocator.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * Copyright (c) 2019-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, diff --git a/libraries/libstratosphere/include/stratosphere/psc.hpp b/libraries/libstratosphere/include/stratosphere/psc.hpp new file mode 100644 index 000000000..3fd62e6f2 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/psc.hpp @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/psc/psc_pm_module.hpp b/libraries/libstratosphere/include/stratosphere/psc/psc_pm_module.hpp new file mode 100644 index 000000000..7b9ca8025 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/psc/psc_pm_module.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +#if defined(ATMOSPHERE_OS_HORIZON) + #include +#else + #error "Unknown OS for psc::PmModule" +#endif diff --git a/libraries/libstratosphere/include/stratosphere/psc/psc_pm_module.os.horizon.hpp b/libraries/libstratosphere/include/stratosphere/psc/psc_pm_module.os.horizon.hpp new file mode 100644 index 000000000..129d57aa6 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/psc/psc_pm_module.os.horizon.hpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include +#include + +namespace ams::psc { + + class PmModule { + NON_COPYABLE(PmModule); + NON_MOVEABLE(PmModule); + private: + std::shared_ptr intf; + os::SystemEvent system_event; + bool initialized; + PmModuleId module_id; + uintptr_t reserved; + public: + PmModule(); + ~PmModule(); + + Result Initialize(const PmModuleId mid, const PmModuleId *dependencies, u32 dependency_count, os::EventClearMode clear_mode); + Result Finalize(); + + constexpr PmModuleId GetId() const { return this->module_id; } + + Result GetRequest(PmState *out_state, PmFlagSet *out_flags); + Result Acknowledge(PmState state, Result res); + + os::SystemEvent *GetEventPointer(); + }; + +} \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/psc/psc_pm_module_id.hpp b/libraries/libstratosphere/include/stratosphere/psc/psc_pm_module_id.hpp new file mode 100644 index 000000000..61196a79c --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/psc/psc_pm_module_id.hpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +namespace ams::psc { + + enum PmModuleId : u16 { + PmModuleId_Usb = 4, + PmModuleId_Ethernet = 5, + PmModuleId_Fgm = 6, + PmModuleId_PcvClock = 7, + PmModuleId_PcvVoltage = 8, + PmModuleId_Gpio = 9, + PmModuleId_Pinmux = 10, + PmModuleId_Uart = 11, + PmModuleId_I2c = 12, + PmModuleId_I2cPcv = 13, + PmModuleId_Spi = 14, + PmModuleId_Pwm = 15, + PmModuleId_Psm = 16, + PmModuleId_Tc = 17, + PmModuleId_Omm = 18, + PmModuleId_Pcie = 19, + PmModuleId_Lbl = 20, + PmModuleId_Display = 21, + + PmModuleId_Hid = 24, + PmModuleId_WlanSockets = 25, + + PmModuleId_Fs = 27, + PmModuleId_Audio = 28, + + PmModuleId_TmaHostIo = 30, + PmModuleId_Bluetooth = 31, + PmModuleId_Bpc = 32, + PmModuleId_Fan = 33, + PmModuleId_Pcm = 34, + PmModuleId_Nfc = 35, + PmModuleId_Apm = 36, + PmModuleId_Btm = 37, + PmModuleId_Nifm = 38, + PmModuleId_GpioLow = 39, + PmModuleId_Npns = 40, + PmModuleId_Lm = 41, + PmModuleId_Bcat = 42, + PmModuleId_Time = 43, + PmModuleId_Pctl = 44, + PmModuleId_Erpt = 45, + PmModuleId_Eupld = 46, + PmModuleId_Friends = 47, + PmModuleId_Bgtc = 48, + PmModuleId_Account = 49, + PmModuleId_Sasbus = 50, + PmModuleId_Ntc = 51, + PmModuleId_Idle = 52, + PmModuleId_Tcap = 53, + PmModuleId_PsmLow = 54, + PmModuleId_Ndd = 55, + PmModuleId_Olsc = 56, + + PmModuleId_Ns = 61, + + PmModuleId_Nvservices = 101, + + PmModuleId_Spsm = 127, + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/psc/psc_types.hpp b/libraries/libstratosphere/include/stratosphere/psc/psc_types.hpp new file mode 100644 index 000000000..3902552d4 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/psc/psc_types.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +namespace ams::psc { + + enum PmState { + PmState_Awake = 0, + PmState_ReadyAwaken = 1, + PmState_ReadySleep = 2, + PmState_ReadySleepCritical = 3, + PmState_ReadyAwakenCritical = 4, + PmState_ReadyShutdown = 5, + }; + + constexpr inline u32 MaximumDependencyLevels = 20; + + struct PmFlag { + + }; + + using PmFlagSet = util::BitFlagSet; + +} diff --git a/libraries/libstratosphere/include/stratosphere/psc/sf/psc_sf_i_pm_module.hpp b/libraries/libstratosphere/include/stratosphere/psc/sf/psc_sf_i_pm_module.hpp new file mode 100644 index 000000000..1f37b0fe3 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/psc/sf/psc_sf_i_pm_module.hpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include + +namespace ams::psc::sf { + + class IPmModule : public ams::sf::IServiceObject { + protected: + enum class CommandId { + Initialize = 0, + GetRequest = 1, + Acknowledge = 2, + Finalize = 3, + AcknowledgeEx = 4, + }; + public: + /* Actual commands. */ + virtual Result Initialize(ams::sf::OutCopyHandle out, psc::PmModuleId module_id, const ams::sf::InBuffer &child_list) = 0; + virtual Result GetRequest(ams::sf::Out out_state, ams::sf::Out out_flags) = 0; + virtual Result Acknowledge() = 0; + virtual Result Finalize() = 0; + virtual Result AcknowledgeEx(PmState state) = 0; + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MAKE_SERVICE_COMMAND_META(Initialize), + MAKE_SERVICE_COMMAND_META(GetRequest), + MAKE_SERVICE_COMMAND_META(Acknowledge), + MAKE_SERVICE_COMMAND_META(Finalize), + MAKE_SERVICE_COMMAND_META(AcknowledgeEx, hos::Version_600), /* TODO: This is really 5.1.0... */ + }; + }; + +} \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/psc/sf/psc_sf_i_pm_service.hpp b/libraries/libstratosphere/include/stratosphere/psc/sf/psc_sf_i_pm_service.hpp new file mode 100644 index 000000000..f4bf8cc8b --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/psc/sf/psc_sf_i_pm_service.hpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +namespace ams::psc::sf { + + class IPmService : public ams::sf::IServiceObject { + protected: + enum class CommandId { + Initialize = 0, + }; + public: + /* Actual commands. */ + virtual Result Initialize(ams::sf::Out> out) = 0; + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MAKE_SERVICE_COMMAND_META(Initialize), + }; + }; + +} \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/settings.hpp b/libraries/libstratosphere/include/stratosphere/settings.hpp index 45205157c..8c95dc127 100644 --- a/libraries/libstratosphere/include/stratosphere/settings.hpp +++ b/libraries/libstratosphere/include/stratosphere/settings.hpp @@ -19,3 +19,7 @@ #include "settings/settings_types.hpp" #include "settings/settings_fwdbg_types.hpp" #include "settings/settings_fwdbg_api.hpp" +#include "settings/system/settings_firmware_version.hpp" +#include "settings/system/settings_product_model.hpp" +#include "settings/system/settings_region.hpp" +#include "settings/system/settings_serial_number.hpp" diff --git a/libraries/libstratosphere/include/stratosphere/settings/system/settings_firmware_version.hpp b/libraries/libstratosphere/include/stratosphere/settings/system/settings_firmware_version.hpp new file mode 100644 index 000000000..dc821b948 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/settings/system/settings_firmware_version.hpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +namespace ams::settings::system { + + struct alignas(4) FirmwareVersion { + u8 major; + u8 minor; + u8 micro; + u8 padding1; + u8 revision_major; + u8 revision_minor; + u8 padding2; + u8 padding3; + char platform[0x20]; + char revision[0x40]; + char display_version[0x18]; + char display_name[0x80]; + + constexpr int GetComparableVersion() const { + return (static_cast(major) << 16) | (static_cast(minor) << 8) | (static_cast(micro) << 0); + } + + constexpr friend bool operator==(const FirmwareVersion &lhs, const FirmwareVersion &rhs) { + return lhs.GetComparableVersion() == rhs.GetComparableVersion(); + } + + constexpr friend bool operator!=(const FirmwareVersion &lhs, const FirmwareVersion &rhs) { + return lhs.GetComparableVersion() != rhs.GetComparableVersion(); + } + + constexpr friend bool operator<=(const FirmwareVersion &lhs, const FirmwareVersion &rhs) { + return lhs.GetComparableVersion() <= rhs.GetComparableVersion(); + } + + constexpr friend bool operator>=(const FirmwareVersion &lhs, const FirmwareVersion &rhs) { + return lhs.GetComparableVersion() >= rhs.GetComparableVersion(); + } + + constexpr friend bool operator<(const FirmwareVersion &lhs, const FirmwareVersion &rhs) { + return lhs.GetComparableVersion() < rhs.GetComparableVersion(); + } + + constexpr friend bool operator>(const FirmwareVersion &lhs, const FirmwareVersion &rhs) { + return lhs.GetComparableVersion() > rhs.GetComparableVersion(); + } + }; + + void GetFirmwareVersion(FirmwareVersion *out); + +} diff --git a/libraries/libstratosphere/include/stratosphere/settings/system/settings_product_model.hpp b/libraries/libstratosphere/include/stratosphere/settings/system/settings_product_model.hpp new file mode 100644 index 000000000..9bf655e96 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/settings/system/settings_product_model.hpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +namespace ams::settings::system { + + enum ProductModel { + ProductModel_Invalid = 0, + ProductModel_Nx = 1, + }; + + ProductModel GetProductModel(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/settings/system/settings_region.hpp b/libraries/libstratosphere/include/stratosphere/settings/system/settings_region.hpp new file mode 100644 index 000000000..c756e45a0 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/settings/system/settings_region.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +namespace ams::settings::system { + + enum RegionCode { + RegionCode_Japan = 0, + RegionCode_Usa = 1, + RegionCode_Europe = 2, + RegionCode_Australia = 3, + RegionCode_HongKongTaiwanKorea = 4, + RegionCode_China = 5, + }; + + void GetRegionCode(RegionCode *out); + +} diff --git a/libraries/libstratosphere/include/stratosphere/settings/system/settings_serial_number.hpp b/libraries/libstratosphere/include/stratosphere/settings/system/settings_serial_number.hpp new file mode 100644 index 000000000..087bda0d5 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/settings/system/settings_serial_number.hpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +namespace ams::settings::system { + + struct SerialNumber { + char str[0x18]; + }; + + void GetSerialNumber(SerialNumber *out); + +} diff --git a/libraries/libstratosphere/include/stratosphere/time.hpp b/libraries/libstratosphere/include/stratosphere/time.hpp new file mode 100644 index 000000000..26349ec08 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/time.hpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/time/impl/util/time_impl_util_api.hpp b/libraries/libstratosphere/include/stratosphere/time/impl/util/time_impl_util_api.hpp new file mode 100644 index 000000000..464a43baf --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/time/impl/util/time_impl_util_api.hpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include + +namespace ams::time::impl::util { + + Result GetSpanBetween(s64 *out, const SteadyClockTimePoint &from, const SteadyClockTimePoint &to); + +} diff --git a/libraries/libstratosphere/include/stratosphere/time/time_api.hpp b/libraries/libstratosphere/include/stratosphere/time/time_api.hpp new file mode 100644 index 000000000..1784ea93b --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/time/time_api.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include + +namespace ams::time { + + Result Initialize(); + Result InitializeForSystem(); + Result InitializeForSystemUser(); + + Result Finalize(); + + bool IsInitialized(); + + Result GetElapsedSecondsBetween(s64 *out, const SteadyClockTimePoint &from, const SteadyClockTimePoint &to); + +} diff --git a/libraries/libstratosphere/include/stratosphere/time/time_common.hpp b/libraries/libstratosphere/include/stratosphere/time/time_common.hpp new file mode 100644 index 000000000..fb7e37034 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/time/time_common.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::time { + + using SourceId = util::Uuid; + +} diff --git a/libraries/libstratosphere/include/stratosphere/time/time_posix_time.hpp b/libraries/libstratosphere/include/stratosphere/time/time_posix_time.hpp new file mode 100644 index 000000000..7b95f9c02 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/time/time_posix_time.hpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::time { + + struct PosixTime { + s64 value; + + constexpr PosixTime &operator+=(const TimeSpan &ts) { this->value += ts.GetSeconds(); return *this; } + constexpr PosixTime &operator-=(const TimeSpan &ts) { this->value -= ts.GetSeconds(); return *this; } + + constexpr friend PosixTime operator+(const PosixTime &lhs, const TimeSpan &rhs) { return { .value = lhs.value + rhs.GetSeconds() }; } + constexpr friend PosixTime operator-(const PosixTime &lhs, const TimeSpan &rhs) { return { .value = lhs.value - rhs.GetSeconds() }; } + + constexpr friend TimeSpan operator-(const PosixTime &lhs, const PosixTime &rhs) { return TimeSpan::FromSeconds(lhs.value - rhs.value); } + + constexpr friend bool operator==(const PosixTime &lhs, const PosixTime &rhs) { return lhs.value == rhs.value; } + constexpr friend bool operator!=(const PosixTime &lhs, const PosixTime &rhs) { return lhs.value != rhs.value; } + constexpr friend bool operator<=(const PosixTime &lhs, const PosixTime &rhs) { return lhs.value <= rhs.value; } + constexpr friend bool operator>=(const PosixTime &lhs, const PosixTime &rhs) { return lhs.value >= rhs.value; } + constexpr friend bool operator< (const PosixTime &lhs, const PosixTime &rhs) { return lhs.value < rhs.value; } + constexpr friend bool operator> (const PosixTime &lhs, const PosixTime &rhs) { return lhs.value > rhs.value; } + }; + static_assert(sizeof(PosixTime) == sizeof(s64)); + +} diff --git a/libraries/libstratosphere/include/stratosphere/time/time_standard_network_system_clock.hpp b/libraries/libstratosphere/include/stratosphere/time/time_standard_network_system_clock.hpp new file mode 100644 index 000000000..d9f7815b6 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/time/time_standard_network_system_clock.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include + +namespace ams::time { + + class StandardNetworkSystemClock { + public: + using rep = SystemClockTraits::rep; + using period = SystemClockTraits::period; + using duration = SystemClockTraits::duration; + using time_point = SystemClockTraits::time_point; + static constexpr bool is_steady = false; + public: + static time_point now(); + static std::time_t to_time_t(const StandardUserSystemClock::time_point &t); + static time_point from_time_t(std::time_t t); + public: + static Result GetCurrentTime(PosixTime *out); + /* TODO: static Result GetSystemClockContext(SystemClockContext *out); */ + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/time/time_standard_steady_clock.hpp b/libraries/libstratosphere/include/stratosphere/time/time_standard_steady_clock.hpp new file mode 100644 index 000000000..eee160bb0 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/time/time_standard_steady_clock.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +namespace ams::time { + + class StandardSteadyClock { + public: + using rep = s64; + using period = std::ratio<1>; + using duration = std::chrono::duration; + using time_point = std::chrono::time_point; + static constexpr bool is_steady = true; + public: + static time_point now(); + public: + static Result GetCurrentTimePoint(SteadyClockTimePoint *out); + }; + + Result GetStandardSteadyClockCurrentTimePoint(SteadyClockTimePoint *out); + TimeSpan GetStandardSteadyClockInternalOffset(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/time/time_standard_user_system_clock.hpp b/libraries/libstratosphere/include/stratosphere/time/time_standard_user_system_clock.hpp new file mode 100644 index 000000000..bcc4b1c15 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/time/time_standard_user_system_clock.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include + +namespace ams::time { + + class StandardUserSystemClock { + public: + using rep = SystemClockTraits::rep; + using period = SystemClockTraits::period; + using duration = SystemClockTraits::duration; + using time_point = SystemClockTraits::time_point; + static constexpr bool is_steady = false; + public: + static time_point now(); + static std::time_t to_time_t(const StandardUserSystemClock::time_point &t); + static time_point from_time_t(std::time_t t); + public: + static Result GetCurrentTime(PosixTime *out); + /* TODO: static Result GetSystemClockContext(SystemClockContext *out); */ + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/time/time_steady_clock_time_point.hpp b/libraries/libstratosphere/include/stratosphere/time/time_steady_clock_time_point.hpp new file mode 100644 index 000000000..d54c13420 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/time/time_steady_clock_time_point.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::time { + + struct SteadyClockTimePoint { + s64 value; + SourceId source_id; + + constexpr SteadyClockTimePoint &operator+=(const TimeSpan &ts) { this->value += ts.GetSeconds(); return *this; } + constexpr SteadyClockTimePoint &operator-=(const TimeSpan &ts) { this->value -= ts.GetSeconds(); return *this; } + + constexpr friend SteadyClockTimePoint operator+(const SteadyClockTimePoint &lhs, const TimeSpan &rhs) { return { .value = lhs.value + rhs.GetSeconds(), .source_id = lhs.source_id }; } + constexpr friend SteadyClockTimePoint operator-(const SteadyClockTimePoint &lhs, const TimeSpan &rhs) { return { .value = lhs.value - rhs.GetSeconds(), .source_id = lhs.source_id }; } + + constexpr friend bool operator==(const SteadyClockTimePoint &lhs, const SteadyClockTimePoint &rhs) { return lhs.value == rhs.value && lhs.source_id == rhs.source_id; } + constexpr friend bool operator!=(const SteadyClockTimePoint &lhs, const SteadyClockTimePoint &rhs) { return !(lhs == rhs); } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/time/time_system_clock_common.hpp b/libraries/libstratosphere/include/stratosphere/time/time_system_clock_common.hpp new file mode 100644 index 000000000..d3b057ad0 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/time/time_system_clock_common.hpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::time { + + class SystemClockTraits { + public: + using rep = s64; + using period = std::ratio<1>; + using duration = std::chrono::duration; + using time_point = std::chrono::time_point; + }; + +} diff --git a/libraries/libstratosphere/source/crypto/crypto_csrng.os.horizon.cpp b/libraries/libstratosphere/source/crypto/crypto_csrng.os.horizon.cpp new file mode 100644 index 000000000..4ef1c0ce0 --- /dev/null +++ b/libraries/libstratosphere/source/crypto/crypto_csrng.os.horizon.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::crypto { + + namespace { + + bool g_initialized; + os::Mutex g_lock(false); + + void InitializeCsrng() { + AMS_ASSERT(!g_initialized); + sm::DoWithSession([&]() { + R_ABORT_UNLESS(::csrngInitialize()); + }); + } + + } + + void GenerateCryptographicallyRandomBytes(void *dst, size_t dst_size) { + { + std::scoped_lock lk(g_lock); + + if (AMS_UNLIKELY(!g_initialized)) { + InitializeCsrng(); + g_initialized = true; + } + } + + R_ABORT_UNLESS(csrngGetRandomBytes(dst, dst_size)); + } + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_allocator.hpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_allocator.hpp new file mode 100644 index 000000000..f92e1d4de --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_allocator.hpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::erpt::srv { + + extern lmem::HeapHandle g_heap_handle; + + class Allocator { + public: + void *operator new(size_t sz) { return lmem::AllocateFromExpHeap(g_heap_handle, sz); } + void *operator new(size_t sz, size_t algn) { return lmem::AllocateFromExpHeap(g_heap_handle, sz, static_cast(algn)); } + void *operator new[](size_t sz) { return lmem::AllocateFromExpHeap(g_heap_handle, sz); } + void *operator new[](size_t sz, size_t algn) { return lmem::AllocateFromExpHeap(g_heap_handle, sz, static_cast(algn)); } + + void operator delete(void *p) { lmem::FreeToExpHeap(g_heap_handle, p); } + void operator delete[](void *p) { lmem::FreeToExpHeap(g_heap_handle, p); } + }; + + inline void *Allocate(size_t sz) { + return lmem::AllocateFromExpHeap(g_heap_handle, sz); + } + + inline void *AllocateWithAlign(size_t sz, size_t align) { + return lmem::AllocateFromExpHeap(g_heap_handle, sz, align); + } + + inline void Deallocate(void *p) { + return lmem::FreeToExpHeap(g_heap_handle, p); + } + + inline void DeallocateWithSize(void *p, size_t size) { + return lmem::FreeToExpHeap(g_heap_handle, p); + } + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_attachment.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_attachment.cpp new file mode 100644 index 000000000..8eb35a6ae --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_attachment.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "erpt_srv_attachment_impl.hpp" +#include "erpt_srv_attachment.hpp" + +namespace ams::erpt::srv { + + AttachmentFileName Attachment::FileName(AttachmentId attachment_id) { + char uuid_str[AttachmentFileNameLength]; + attachment_id.uuid.ToString(uuid_str, sizeof(uuid_str)); + + AttachmentFileName attachment_name; + std::snprintf(attachment_name.name, sizeof(attachment_name.name), "%s/%s.att", ReportStoragePath, uuid_str); + return attachment_name; + } + + Attachment::Attachment(JournalRecord *r) : record(r) { + this->record->AddReference(); + } + + Attachment::~Attachment() { + this->CloseStream(); + if (this->record->RemoveReference()) { + this->DeleteStream(this->FileName().name); + delete this->record; + } + } + + AttachmentFileName Attachment::FileName() { + return FileName(this->record->info.attachment_id); + } + + Result Attachment::Open(AttachmentOpenType type) { + switch (type) { + case AttachmentOpenType_Create: return this->OpenStream(this->FileName().name, StreamMode_Write, AttachmentStreamBufferSize); + case AttachmentOpenType_Read: return this->OpenStream(this->FileName().name, StreamMode_Read, AttachmentStreamBufferSize); + default: return erpt::ResultInvalidArgument(); + } + } + + Result Attachment::Read(u32 *out_read_count, u8 *dst, u32 dst_size) { + return this->ReadStream(out_read_count, dst, dst_size); + } + + Result Attachment::Delete() { + return this->DeleteStream(this->FileName().name); + } + + void Attachment::Close() { + return this->CloseStream(); + } + + Result Attachment::GetFlags(AttachmentFlagSet *out) { + *out = this->record->info.flags; + return ResultSuccess(); + } + + Result Attachment::SetFlags(AttachmentFlagSet flags) { + if (((~this->record->info.flags) & flags).IsAnySet()) { + this->record->info.flags |= flags; + return Journal::Commit(); + } + return ResultSuccess(); + } + + Result Attachment::GetSize(s64 *out) { + return this->GetStreamSize(out); + } + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_attachment.hpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_attachment.hpp new file mode 100644 index 000000000..7692cdbe4 --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_attachment.hpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "erpt_srv_allocator.hpp" +#include "erpt_srv_stream.hpp" +#include "erpt_srv_journal.hpp" + +namespace ams::erpt::srv { + + enum AttachmentOpenType { + AttachmentOpenType_Create = 0, + AttachmentOpenType_Read = 1, + }; + + constexpr inline u32 AttachmentStreamBufferSize = 1_KB; + + class Attachment : public Allocator, public Stream { + private: + JournalRecord *record; + private: + AttachmentFileName FileName(); + public: + static AttachmentFileName FileName(AttachmentId attachment_id); + public: + explicit Attachment(JournalRecord *r); + ~Attachment(); + + Result Open(AttachmentOpenType type); + Result Read(u32 *out_read_count, u8 *dst, u32 dst_size); + Result Delete(); + void Close(); + + Result GetFlags(AttachmentFlagSet *out); + Result SetFlags(AttachmentFlagSet flags); + Result GetSize(s64 *out); + + template + Result Write(T val) { + return this->WriteStream(reinterpret_cast(std::addressof(val)), sizeof(val)); + } + + template + Result Write(const T *buf, u32 buffer_size) { + return this->WriteStream(reinterpret_cast(buf), buffer_size); + } + }; + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_attachment_impl.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_attachment_impl.cpp new file mode 100644 index 000000000..b4ce4146c --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_attachment_impl.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "erpt_srv_attachment_impl.hpp" +#include "erpt_srv_attachment.hpp" + +namespace ams::erpt::srv { + + AttachmentImpl::AttachmentImpl() : attachment(nullptr) { + /* ... */ + } + + AttachmentImpl::~AttachmentImpl() { + R_ABORT_UNLESS(this->Close()); + } + + Result AttachmentImpl::Open(const AttachmentId &attachment_id) { + R_UNLESS(this->attachment == nullptr, erpt::ResultAlreadyInitialized()); + + JournalRecord *record = Journal::Retrieve(attachment_id); + R_UNLESS(record != nullptr, erpt::ResultNotFound()); + + this->attachment = new Attachment(record); + R_UNLESS(this->attachment != nullptr, erpt::ResultOutOfMemory()); + auto attachment_guard = SCOPE_GUARD { delete this->attachment; this->attachment = nullptr; }; + + R_TRY(this->attachment->Open(AttachmentOpenType_Read)); + attachment_guard.Cancel(); + + return ResultSuccess(); + } + + Result AttachmentImpl::Read(ams::sf::Out out_count, const ams::sf::OutBuffer &out_buffer) { + R_UNLESS(this->attachment != nullptr, erpt::ResultNotInitialized()); + + return this->attachment->Read(out_count.GetPointer(), static_cast(out_buffer.GetPointer()), static_cast(out_buffer.GetSize())); + } + + Result AttachmentImpl::SetFlags(AttachmentFlagSet flags) { + R_UNLESS(this->attachment != nullptr, erpt::ResultNotInitialized()); + + return this->attachment->SetFlags(flags); + } + + Result AttachmentImpl::GetFlags(ams::sf::Out out) { + R_UNLESS(this->attachment != nullptr, erpt::ResultNotInitialized()); + + return this->attachment->GetFlags(out.GetPointer()); + } + + Result AttachmentImpl::Close() { + if (this->attachment != nullptr) { + this->attachment->Close(); + delete this->attachment; + this->attachment = nullptr; + } + return ResultSuccess(); + } + + Result AttachmentImpl::GetSize(ams::sf::Out out) { + R_UNLESS(this->attachment != nullptr, erpt::ResultNotInitialized()); + + return this->attachment->GetSize(out.GetPointer()); + } + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_attachment_impl.hpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_attachment_impl.hpp new file mode 100644 index 000000000..8d0e65670 --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_attachment_impl.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::erpt::srv { + + class Attachment; + + class AttachmentImpl final : public erpt::sf::IAttachment { + private: + Attachment *attachment; + public: + AttachmentImpl(); + ~AttachmentImpl(); + public: + virtual Result Open(const AttachmentId &attachment_id) override final; + virtual Result Read(ams::sf::Out out_count, const ams::sf::OutBuffer &out_buffer) override final; + virtual Result SetFlags(AttachmentFlagSet flags) override final; + virtual Result GetFlags(ams::sf::Out out) override final; + virtual Result Close() override final; + virtual Result GetSize(ams::sf::Out out) override final; + }; + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_cipher.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_cipher.cpp new file mode 100644 index 000000000..c370927cc --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_cipher.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "erpt_srv_cipher.hpp" + +namespace ams::erpt::srv { + + u8 Cipher::s_key[crypto::Aes128CtrEncryptor::KeySize + crypto::Aes128CtrEncryptor::IvSize + crypto::Aes128CtrEncryptor::BlockSize]; + bool Cipher::s_need_to_store_cipher = false; + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_cipher.hpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_cipher.hpp new file mode 100644 index 000000000..8a26c1cce --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_cipher.hpp @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "erpt_srv_formatter.hpp" +#include "erpt_srv_keys.hpp" + +namespace ams::erpt::srv { + + class Cipher : private Formatter { + private: + static constexpr u32 RsaKeySize = 0x100; + static constexpr u32 SaltSize = 0x20; + + static u8 s_key[crypto::Aes128CtrEncryptor::KeySize + crypto::Aes128CtrEncryptor::IvSize + crypto::Aes128CtrEncryptor::BlockSize]; + static bool s_need_to_store_cipher; + + struct Header { + u32 magic; + u32 field_type; + u32 element_count; + u32 reserved; + u8 data[0]; + }; + static_assert(sizeof(Header) == 0x10); + + static constexpr u32 HeaderMagic = util::FourCC<'C', 'R', 'P', 'T'>::Code; + private: + template + static Result EncryptArray(Report *report, FieldId field_id, T *arr, u32 arr_size) { + const u32 data_size = util::AlignUp(arr_size * sizeof(T), crypto::Aes128CtrEncryptor::BlockSize); + + Header *hdr = reinterpret_cast
(AllocateWithAlign(sizeof(Header) + data_size, crypto::Aes128CtrEncryptor::BlockSize)); + R_UNLESS(hdr != nullptr, erpt::ResultOutOfMemory()); + ON_SCOPE_EXIT { Deallocate(hdr); }; + + hdr->magic = HeaderMagic; + hdr->field_type = static_cast(FieldToTypeMap[field_id]); + hdr->element_count = arr_size; + hdr->reserved = 0; + + std::memset(hdr->data, 0, data_size); + std::memcpy(hdr->data, arr, arr_size * sizeof(T)); + + crypto::EncryptAes128Ctr(hdr->data, data_size, s_key, crypto::Aes128CtrEncryptor::KeySize, s_key + crypto::Aes128CtrEncryptor::KeySize, crypto::Aes128CtrEncryptor::IvSize, hdr->data, data_size); + + ON_SCOPE_EXIT { std::memset(hdr, 0, sizeof(hdr) + data_size); s_need_to_store_cipher = true; }; + + return Formatter::AddField(report, field_id, reinterpret_cast(hdr), sizeof(hdr) + data_size); + } + public: + static Result Begin(Report *report, u32 record_count) { + s_need_to_store_cipher = false; + crypto::GenerateCryptographicallyRandomBytes(s_key, sizeof(s_key)); + + return Formatter::Begin(report, record_count + 1); + } + + static Result End(Report *report) { + u8 cipher[RsaKeySize] = {}; + + if (s_need_to_store_cipher) { + u8 salt[SaltSize]; + crypto::RsaOaepEncryptor oaep; + crypto::GenerateCryptographicallyRandomBytes(salt, sizeof(salt)); + + oaep.Initialize(GetPublicKeyModulus(), GetPublicKeyModulusSize(), GetPublicKeyExponent(), GetPublicKeyExponentSize()); + oaep.Encrypt(cipher, sizeof(cipher), s_key, sizeof(s_key), salt, sizeof(salt)); + } + + Formatter::AddField(report, FieldId_CipherKey, cipher, sizeof(cipher)); + std::memset(s_key, 0, sizeof(s_key)); + + return Formatter::End(report); + } + + static Result AddField(Report *report, FieldId field_id, bool value) { + return Formatter::AddField(report, field_id, value); + } + + template + static Result AddField(Report *report, FieldId field_id, T value) { + return Formatter::AddField(report, field_id, value); + } + + static Result AddField(Report *report, FieldId field_id, char *str, u32 len) { + if (FieldToFlagMap[field_id] == FieldFlag_Encrypt) { + return EncryptArray(report, field_id, str, len); + } else { + return Formatter::AddField(report, field_id, str, len); + } + } + + static Result AddField(Report *report, FieldId field_id, u8 *bin, u32 len) { + if (FieldToFlagMap[field_id] == FieldFlag_Encrypt) { + return EncryptArray(report, field_id, bin, len); + } else { + return Formatter::AddField(report, field_id, bin, len); + } + } + + template + static Result AddField(Report *report, FieldId field_id, T *arr, u32 len) { + if (FieldToFlagMap[field_id] == FieldFlag_Encrypt) { + return EncryptArray(report, field_id, arr, len); + } else { + return Formatter::AddField(report, field_id, arr, len); + } + } + }; + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_context.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_context.cpp new file mode 100644 index 000000000..1057b25cb --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_context.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "erpt_srv_context.hpp" +#include "erpt_srv_cipher.hpp" +#include "erpt_srv_context_record.hpp" +#include "erpt_srv_report.hpp" + +namespace ams::erpt::srv { + + namespace { + + using ContextList = util::IntrusiveListBaseTraits::ListType; + + ContextList g_category_list; + + } + + Context::Context(CategoryId cat, u32 max_records) : category(cat), max_record_count(max_records), record_count(0) { + g_category_list.push_front(*this); + } + + Context::~Context() { + g_category_list.erase(g_category_list.iterator_to(*this)); + } + + Result Context::AddCategoryToReport(Report *report) { + R_SUCCEED_IF(this->record_list.empty()); + + for (auto it = this->record_list.begin(); it != this->record_list.end(); it++) { + for (u32 i = 0; i < it->ctx.field_count; i++) { + auto *field = std::addressof(it->ctx.fields[i]); + u8 *arr_buf = it->ctx.array_buffer; + + switch (field->type) { + case FieldType_Bool: R_TRY(Cipher::AddField(report, field->id, field->value_bool)); break; + case FieldType_NumericU8: R_TRY(Cipher::AddField(report, field->id, field->value_u8)); break; + case FieldType_NumericU16: R_TRY(Cipher::AddField(report, field->id, field->value_u16)); break; + case FieldType_NumericU32: R_TRY(Cipher::AddField(report, field->id, field->value_u32)); break; + case FieldType_NumericU64: R_TRY(Cipher::AddField(report, field->id, field->value_u64)); break; + case FieldType_NumericI8: R_TRY(Cipher::AddField(report, field->id, field->value_i8)); break; + case FieldType_NumericI16: R_TRY(Cipher::AddField(report, field->id, field->value_i16)); break; + case FieldType_NumericI32: R_TRY(Cipher::AddField(report, field->id, field->value_i32)); break; + case FieldType_NumericI64: R_TRY(Cipher::AddField(report, field->id, field->value_i64)); break; + case FieldType_String: R_TRY(Cipher::AddField(report, field->id, reinterpret_cast(arr_buf + field->value_array.start_idx), field->value_array.size / sizeof(char))); break; + case FieldType_U8Array: R_TRY(Cipher::AddField(report, field->id, reinterpret_cast< u8 *>(arr_buf + field->value_array.start_idx), field->value_array.size / sizeof(u8))); break; + case FieldType_U32Array: R_TRY(Cipher::AddField(report, field->id, reinterpret_cast< u32 *>(arr_buf + field->value_array.start_idx), field->value_array.size / sizeof(u32))); break; + case FieldType_U64Array: R_TRY(Cipher::AddField(report, field->id, reinterpret_cast< u64 *>(arr_buf + field->value_array.start_idx), field->value_array.size / sizeof(u64))); break; + case FieldType_I8Array: R_TRY(Cipher::AddField(report, field->id, reinterpret_cast< s8 *>(arr_buf + field->value_array.start_idx), field->value_array.size / sizeof(s8))); break; + case FieldType_I32Array: R_TRY(Cipher::AddField(report, field->id, reinterpret_cast< s32 *>(arr_buf + field->value_array.start_idx), field->value_array.size / sizeof(s32))); break; + case FieldType_I64Array: R_TRY(Cipher::AddField(report, field->id, reinterpret_cast< s64 *>(arr_buf + field->value_array.start_idx), field->value_array.size / sizeof(s64))); break; + default: return erpt::ResultInvalidArgument(); + } + } + } + + return ResultSuccess(); + } + + Result Context::AddContextToCategory(const ContextEntry *entry, const u8 *data, u32 data_size) { + ContextRecord *record = new ContextRecord(); + R_UNLESS(record != nullptr, erpt::ResultOutOfMemory()); + auto guard = SCOPE_GUARD { delete record; }; + + R_TRY(record->Initialize(entry, data, data_size)); + + guard.Cancel(); + this->AddContextRecordToCategory(record); + return ResultSuccess(); + } + + Result Context::AddContextRecordToCategory(ContextRecord *record) { + if (this->record_count < this->max_record_count) { + this->record_list.push_front(*record); + this->record_count++; + } else { + ContextRecord *back = std::addressof(this->record_list.back()); + this->record_list.pop_back(); + this->record_list.push_front(*record); + delete back; + } + + return ResultSuccess(); + } + + Result Context::SubmitContext(const ContextEntry *entry, const u8 *data, u32 data_size) { + for (auto it = g_category_list.begin(); it != g_category_list.end(); it++) { + if (it->category == entry->category) { + return it->AddContextToCategory(entry, data, data_size); + } + } + return erpt::ResultCategoryNotFound(); + } + + Result Context::SubmitContextRecord(ContextRecord *record) { + for (auto it = g_category_list.begin(); it != g_category_list.end(); it++) { + if (it->category == record->ctx.category) { + return it->AddContextRecordToCategory(record); + } + } + return erpt::ResultCategoryNotFound(); + } + + Result Context::WriteContextsToReport(Report *report) { + R_TRY(report->Open(ReportOpenType_Create)); + R_TRY(Cipher::Begin(report, ContextRecord::GetRecordCount())); + + for (auto it = g_category_list.begin(); it != g_category_list.end(); it++) { + R_TRY(it->AddCategoryToReport(report)); + } + + Cipher::End(report); + report->Close(); + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_context.hpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_context.hpp new file mode 100644 index 000000000..f6430ab63 --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_context.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "erpt_srv_allocator.hpp" +#include "erpt_srv_cipher.hpp" + +namespace ams::erpt::srv { + + class ContextRecord; + class Report; + + class Context : public Allocator, public util::IntrusiveListBaseNode { + private: + const CategoryId category; + const u32 max_record_count; + u32 record_count; + util::IntrusiveListBaseTraits::ListType record_list; + public: + Context(CategoryId cat, u32 max_records); + ~Context(); + + Result AddCategoryToReport(Report *report); + Result AddContextToCategory(const ContextEntry *entry, const u8 *data, u32 data_size); + Result AddContextRecordToCategory(ContextRecord *record); + public: + static Result SubmitContext(const ContextEntry *entry, const u8 *data, u32 data_size); + static Result SubmitContextRecord(ContextRecord *record); + static Result WriteContextsToReport(Report *report); + }; + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_impl.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_impl.cpp new file mode 100644 index 000000000..d235aebe5 --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_impl.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "erpt_srv_context_impl.hpp" +#include "erpt_srv_manager_impl.hpp" +#include "erpt_srv_context.hpp" +#include "erpt_srv_reporter.hpp" +#include "erpt_srv_journal.hpp" + +namespace ams::erpt::srv { + + Result ContextImpl::SubmitContext(const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &data_buffer) { + const ContextEntry *ctx = reinterpret_cast( ctx_buffer.GetPointer()); + const u8 *data = reinterpret_cast(data_buffer.GetPointer()); + + const u32 ctx_size = static_cast(ctx_buffer.GetSize()); + const u32 data_size = static_cast(data_buffer.GetSize()); + + R_UNLESS(ctx_size == sizeof(ContextEntry), erpt::ResultInvalidArgument()); + R_UNLESS(data_size <= ArrayBufferSizeMax, erpt::ResultInvalidArgument()); + + return Context::SubmitContext(ctx, data, data_size); + } + + Result ContextImpl::CreateReport(ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &data_buffer, const ams::sf::InBuffer &meta_buffer) { + const ContextEntry *ctx = reinterpret_cast( ctx_buffer.GetPointer()); + const u8 *data = reinterpret_cast(data_buffer.GetPointer()); + const ReportMetaData *meta = reinterpret_cast(meta_buffer.GetPointer()); + + const u32 ctx_size = static_cast(ctx_buffer.GetSize()); + const u32 data_size = static_cast(data_buffer.GetSize()); + const u32 meta_size = static_cast(meta_buffer.GetSize()); + + R_UNLESS(ctx_size == sizeof(ContextEntry), erpt::ResultInvalidArgument()); + R_UNLESS(meta_size == 0 || meta_size == sizeof(ReportMetaData), erpt::ResultInvalidArgument()); + + Reporter reporter(report_type, ctx, data, data_size, meta_size != 0 ? meta : nullptr, nullptr, 0); + R_TRY(reporter.CreateReport()); + + ManagerImpl::NotifyAll(); + + return ResultSuccess(); + } + + Result ContextImpl::SetInitialLaunchSettingsCompletionTime(const time::SteadyClockTimePoint &time_point) { + Reporter::SetInitialLaunchSettingsCompletionTime(time_point); + return ResultSuccess(); + } + + Result ContextImpl::ClearInitialLaunchSettingsCompletionTime() { + Reporter::ClearInitialLaunchSettingsCompletionTime(); + return ResultSuccess(); + } + + Result ContextImpl::UpdatePowerOnTime() { + Reporter::UpdatePowerOnTime(); + return ResultSuccess(); + } + + Result ContextImpl::UpdateAwakeTime() { + Reporter::UpdateAwakeTime(); + return ResultSuccess(); + } + + Result ContextImpl::SubmitMultipleCategoryContext(const MultipleCategoryContextEntry &ctx_entry, const ams::sf::InBuffer &str_buffer) { + R_UNLESS(0 <= ctx_entry.category_count && ctx_entry.category_count <= CategoriesPerMultipleCategoryContext, erpt::ResultInvalidArgument()); + + const u8 *str = reinterpret_cast(str_buffer.GetPointer()); + const u32 str_size = static_cast(str_buffer.GetSize()); + + u32 total_field_count = 0, total_arr_count = 0; + for (u32 i = 0; i < ctx_entry.category_count; i++) { + ContextEntry entry = { + .version = ctx_entry.version, + .field_count = ctx_entry.field_counts[i], + .category = ctx_entry.categories[i], + }; + R_UNLESS(entry.field_count <= erpt::FieldsPerContext, erpt::ResultInvalidArgument()); + R_UNLESS(entry.field_count + total_field_count <= erpt::FieldsPerMultipleCategoryContext, erpt::ResultInvalidArgument()); + R_UNLESS(ctx_entry.array_buf_counts[i] <= ArrayBufferSizeMax, erpt::ResultInvalidArgument()); + R_UNLESS(ctx_entry.array_buf_counts[i] + total_arr_count <= str_size, erpt::ResultInvalidArgument()); + + std::memcpy(entry.fields, ctx_entry.fields + total_field_count, entry.field_count * sizeof(FieldEntry)); + + R_TRY(Context::SubmitContext(std::addressof(entry), str + total_arr_count, ctx_entry.array_buf_counts[i])); + + total_field_count += entry.field_count; + total_arr_count += ctx_entry.array_buf_counts[i]; + } + + return ResultSuccess(); + } + + Result ContextImpl::UpdateApplicationLaunchTime() { + Reporter::UpdateApplicationLaunchTime(); + return ResultSuccess(); + } + + Result ContextImpl::ClearApplicationLaunchTime() { + Reporter::ClearApplicationLaunchTime(); + return ResultSuccess(); + } + + Result ContextImpl::SubmitAttachment(ams::sf::Out out, const ams::sf::InBuffer &attachment_name, const ams::sf::InBuffer &attachment_data) { + const char *name = reinterpret_cast(attachment_name.GetPointer()); + const u8 *data = reinterpret_cast(attachment_data.GetPointer()); + + const u32 name_size = static_cast(attachment_name.GetSize()); + const u32 data_size = static_cast(attachment_data.GetSize()); + + R_UNLESS(data != nullptr, erpt::ResultInvalidArgument()); + R_UNLESS(data_size <= AttachmentSizeMax, erpt::ResultInvalidArgument()); + R_UNLESS(name != nullptr, erpt::ResultInvalidArgument()); + R_UNLESS(name_size <= AttachmentNameSizeMax, erpt::ResultInvalidArgument()); + + char name_safe[AttachmentNameSizeMax]; + util::Strlcpy(name_safe, name, sizeof(name_safe)); + + return JournalForAttachments::SubmitAttachment(out.GetPointer(), name_safe, data, data_size); + } + + Result ContextImpl::CreateReportWithAttachments(ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &data_buffer, const ams::sf::InBuffer &attachment_ids_buffer) { + const ContextEntry *ctx = reinterpret_cast( ctx_buffer.GetPointer()); + const u8 *data = reinterpret_cast(data_buffer.GetPointer()); + const u32 ctx_size = static_cast(ctx_buffer.GetSize()); + const u32 data_size = static_cast(data_buffer.GetSize()); + + const AttachmentId *attachments = reinterpret_cast(attachment_ids_buffer.GetPointer()); + const u32 num_attachments = attachment_ids_buffer.GetSize() / sizeof(*attachments); + + R_UNLESS(ctx_size == sizeof(ContextEntry), erpt::ResultInvalidArgument()); + R_UNLESS(num_attachments <= AttachmentsPerReportMax, erpt::ResultInvalidArgument()); + + Reporter reporter(report_type, ctx, data, data_size, nullptr, attachments, num_attachments); + R_TRY(reporter.CreateReport()); + + ManagerImpl::NotifyAll(); + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_impl.hpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_impl.hpp new file mode 100644 index 000000000..662f02022 --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_impl.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::erpt::srv { + + class ContextImpl final : public erpt::sf::IContext { + public: + virtual Result SubmitContext(const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &data_buffer) override final; + virtual Result CreateReport(ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &data_buffer, const ams::sf::InBuffer &meta_buffer) override final; + virtual Result SetInitialLaunchSettingsCompletionTime(const time::SteadyClockTimePoint &time_point) override final; + virtual Result ClearInitialLaunchSettingsCompletionTime() override final; + virtual Result UpdatePowerOnTime() override final; + virtual Result UpdateAwakeTime() override final; + virtual Result SubmitMultipleCategoryContext(const MultipleCategoryContextEntry &ctx_entry, const ams::sf::InBuffer &str_buffer) override final; + virtual Result UpdateApplicationLaunchTime() override final; + virtual Result ClearApplicationLaunchTime() override final; + virtual Result SubmitAttachment(ams::sf::Out out, const ams::sf::InBuffer &attachment_name, const ams::sf::InBuffer &attachment_data) override final; + virtual Result CreateReportWithAttachments(ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &data_buffer, const ams::sf::InBuffer &attachment_ids_buffer) override final; + }; + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_record.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_record.cpp new file mode 100644 index 000000000..ace1c0fc0 --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_record.cpp @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "erpt_srv_context_record.hpp" + +namespace ams::erpt::srv { + + u32 ContextRecord::s_record_count = 0; + + namespace { + + bool IsArrayFieldType(FieldType type) { + return type == FieldType_String || + type == FieldType_U8Array || + type == FieldType_U32Array || + type == FieldType_U64Array || + type == FieldType_I32Array || + type == FieldType_I64Array; + } + + } + + ContextRecord::ContextRecord() { + this->ctx = {}; + } + + ContextRecord::ContextRecord(CategoryId category) { + this->ctx = { + .category = category, + .array_buffer = static_cast(Allocate(ArrayBufferSizeDefault)), + }; + if (this->ctx.array_buffer != nullptr) { + this->ctx.array_buffer_size = ArrayBufferSizeDefault; + this->ctx.array_free_count = ArrayBufferSizeDefault; + } + } + + ContextRecord::~ContextRecord() { + if (this->ctx.array_buffer != nullptr) { + Deallocate(this->ctx.array_buffer); + } + + AMS_ABORT_UNLESS(s_record_count >= this->ctx.field_count); + s_record_count -= this->ctx.field_count; + } + + Result ContextRecord::Initialize(const ContextEntry *ctx_ptr, const u8 *data, u32 data_size) { + R_UNLESS(data_size <= ArrayBufferSizeMax, erpt::ResultInvalidArgument()); + + this->ctx.version = ctx_ptr->version; + this->ctx.field_count = ctx_ptr->field_count; + this->ctx.category = ctx_ptr->category; + this->ctx.array_buffer = nullptr; + this->ctx.array_buffer_size = data_size; + this->ctx.array_free_count = 0; + + auto guard = SCOPE_GUARD { this->ctx.field_count = 0; }; + + R_UNLESS(this->ctx.field_count <= FieldsPerContext, erpt::ResultInvalidArgument()); + R_UNLESS(0 <= this->ctx.category && this->ctx.category < CategoryId_Count, erpt::ResultInvalidArgument()); + + for (u32 i = 0; i < this->ctx.field_count; i++) { + this->ctx.fields[i] = ctx_ptr->fields[i]; + + R_UNLESS(0 <= this->ctx.fields[i].id && this->ctx.fields[i].id < FieldId_Count, erpt::ResultInvalidArgument()); + R_UNLESS(0 <= this->ctx.fields[i].type && this->ctx.fields[i].type < FieldType_Count, erpt::ResultInvalidArgument()); + + R_UNLESS(this->ctx.fields[i].type == FieldToTypeMap[this->ctx.fields[i].id], erpt::ResultFieldTypeMismatch()); + R_UNLESS(this->ctx.category == FieldToCategoryMap[this->ctx.fields[i].id], erpt::ResultFieldCategoryMismatch()); + + if (IsArrayFieldType(this->ctx.fields[i].type)) { + const u32 start_idx = this->ctx.fields[i].value_array.start_idx; + const u32 size = this->ctx.fields[i].value_array.size; + const u32 end_idx = start_idx + size; + + R_UNLESS(start_idx <= data_size, erpt::ResultInvalidArgument()); + R_UNLESS(size <= data_size, erpt::ResultInvalidArgument()); + R_UNLESS(end_idx <= data_size, erpt::ResultInvalidArgument()); + R_UNLESS(size <= ArrayFieldSizeMax, erpt::ResultInvalidArgument()); + } + } + + if (data_size > 0) { + this->ctx.array_buffer = static_cast(AllocateWithAlign(data_size, alignof(u64))); + R_UNLESS(this->ctx.array_buffer != nullptr, erpt::ResultOutOfMemory()); + + std::memcpy(this->ctx.array_buffer, data, data_size); + } + + guard.Cancel(); + s_record_count += this->ctx.field_count; + + return ResultSuccess(); + } + + Result ContextRecord::Add(FieldId field_id, bool value_bool) { + R_UNLESS(this->ctx.field_count < FieldsPerContext, erpt::ResultOutOfFieldSpace()); + + s_record_count++; + auto &field = this->ctx.fields[this->ctx.field_count++]; + + field.id = field_id; + field.type = FieldType_Bool; + + field.value_bool = value_bool; + + return ResultSuccess(); + } + + Result ContextRecord::Add(FieldId field_id, u32 value_u32) { + R_UNLESS(this->ctx.field_count < FieldsPerContext, erpt::ResultOutOfFieldSpace()); + + s_record_count++; + auto &field = this->ctx.fields[this->ctx.field_count++]; + + field.id = field_id; + field.type = FieldType_NumericU32; + + field.value_u32 = value_u32; + + return ResultSuccess(); + } + + Result ContextRecord::Add(FieldId field_id, u64 value_u64) { + R_UNLESS(this->ctx.field_count < FieldsPerContext, erpt::ResultOutOfFieldSpace()); + + s_record_count++; + auto &field = this->ctx.fields[this->ctx.field_count++]; + + field.id = field_id; + field.type = FieldType_NumericU64; + + field.value_u64 = value_u64; + + return ResultSuccess(); + } + + Result ContextRecord::Add(FieldId field_id, s32 value_i32) { + R_UNLESS(this->ctx.field_count < FieldsPerContext, erpt::ResultOutOfFieldSpace()); + + s_record_count++; + auto &field = this->ctx.fields[this->ctx.field_count++]; + + field.id = field_id; + field.type = FieldType_NumericI32; + + field.value_i32 = value_i32; + + return ResultSuccess(); + } + + Result ContextRecord::Add(FieldId field_id, s64 value_i64) { + R_UNLESS(this->ctx.field_count < FieldsPerContext, erpt::ResultOutOfFieldSpace()); + + s_record_count++; + auto &field = this->ctx.fields[this->ctx.field_count++]; + + field.id = field_id; + field.type = FieldType_NumericI64; + + field.value_i64 = value_i64; + + return ResultSuccess(); + } + + Result ContextRecord::Add(FieldId field_id, const char *str, u32 str_size) { + R_UNLESS(this->ctx.field_count < FieldsPerContext, erpt::ResultOutOfFieldSpace()); + R_UNLESS(str_size <= this->ctx.array_free_count, erpt::ResultOutOfArraySpace()); + + const u32 start_idx = this->ctx.array_buffer_size - this->ctx.array_free_count; + this->ctx.array_free_count -= str_size; + + s_record_count++; + auto &field = this->ctx.fields[this->ctx.field_count++]; + + field.id = field_id; + field.type = FieldType_String; + + field.value_array = { + .start_idx = start_idx, + .size = str_size, + }; + + std::memcpy(this->ctx.array_buffer + start_idx, str, str_size); + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_record.hpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_record.hpp new file mode 100644 index 000000000..f199acca1 --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_record.hpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "erpt_srv_allocator.hpp" + +namespace ams::erpt::srv { + + class Context; + + class ContextRecord : public Allocator, public util::IntrusiveListBaseNode { + friend class Context; + private: + static u32 s_record_count; + public: + static u32 GetRecordCount() { + return s_record_count; + } + private: + ContextEntry ctx; + public: + ContextRecord(); + explicit ContextRecord(CategoryId category); + ~ContextRecord(); + + Result Initialize(const ContextEntry *ctx_ptr, const u8 *data, u32 data_size); + + Result Add(FieldId field_id, bool value_bool); + Result Add(FieldId field_id, u32 value_u32); + Result Add(FieldId field_id, u64 value_u64); + Result Add(FieldId field_id, s32 value_i32); + Result Add(FieldId field_id, s64 value_i64); + Result Add(FieldId field_id, const char *str, u32 str_size); + }; + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_formatter.hpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_formatter.hpp new file mode 100644 index 000000000..c6a369243 --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_formatter.hpp @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "erpt_srv_report.hpp" + +namespace ams::erpt::srv { + + class Formatter { + private: + enum ElementSize { + ElementSize_16 = 16, + ElementSize_32 = 32, + ElementSize_256 = 256, + ElementSize_16384 = 16384, + }; + private: + static ValueTypeTag GetTag(s8) { return ValueTypeTag::I8; } + static ValueTypeTag GetTag(s16) { return ValueTypeTag::I16; } + static ValueTypeTag GetTag(s32) { return ValueTypeTag::I32; } + static ValueTypeTag GetTag(s64) { return ValueTypeTag::I64; } + static ValueTypeTag GetTag(u8) { return ValueTypeTag::U8; } + static ValueTypeTag GetTag(u16) { return ValueTypeTag::U16; } + static ValueTypeTag GetTag(u32) { return ValueTypeTag::U32; } + static ValueTypeTag GetTag(u64) { return ValueTypeTag::U64; } + + static Result AddId(Report *report, FieldId field_id) { + static_assert(MaxFieldStringSize < ElementSize_256); + + const u32 field_len = static_cast(strnlen(FieldString[field_id], MaxFieldStringSize)); + if (field_len < ElementSize_32) { + R_TRY(report->Write(static_cast(static_cast(ValueTypeTag::FixStr) | field_len))); + } else { + R_TRY(report->Write(static_cast(ValueTypeTag::Str8))); + R_TRY(report->Write(static_cast(field_len))); + } + + R_TRY(report->Write(FieldString[field_id], field_len)); + + return ResultSuccess(); + } + + template + static Result AddValue(Report *report, T value) { + const u8 tag = static_cast(GetTag(value)); + + T big_endian_value; + util::StoreBigEndian(std::addressof(big_endian_value), value); + + R_TRY(report->Write(tag)); + R_TRY(report->Write(reinterpret_cast(std::addressof(big_endian_value)), sizeof(big_endian_value))); + + return ResultSuccess(); + } + + template + static Result AddValueArray(Report *report, T *arr, u32 arr_size) { + if (arr_size < ElementSize_16) { + R_TRY(report->Write(static_cast(static_cast(ValueTypeTag::FixArray) | arr_size))); + } else { + R_UNLESS(arr_size < ElementSize_16384, erpt::ResultFormatterError()); + + u16 be_arr_size; + util::StoreBigEndian(std::addressof(be_arr_size), static_cast(arr_size)); + + R_TRY(report->Write(static_cast(ValueTypeTag::Array16))); + R_TRY(report->Write(be_arr_size)); + } + + for (u32 i = 0; i < arr_size; i++) { + R_TRY(AddValue(report, arr[i])); + } + + return ResultSuccess(); + } + + template + static Result AddIdValuePair(Report *report, FieldId field_id, T value) { + R_TRY(AddId(report, field_id)); + R_TRY(AddValue(report, value)); + return ResultSuccess(); + } + + template + static Result AddIdValueArray(Report *report, FieldId field_id, T *arr, u32 arr_size) { + R_TRY(AddId(report, field_id)); + R_TRY(AddValueArray(report, arr, arr_size)); + return ResultSuccess(); + } + public: + static Result Begin(Report *report, u32 record_count) { + if (record_count < ElementSize_16) { + R_TRY(report->Write(static_cast(static_cast(ValueTypeTag::FixMap) | record_count))); + } else { + R_UNLESS(record_count < ElementSize_16384, erpt::ResultFormatterError()); + + u16 be_count; + util::StoreBigEndian(std::addressof(be_count), static_cast(record_count)); + + R_TRY(report->Write(static_cast(ValueTypeTag::Map16))); + R_TRY(report->Write(be_count)); + } + + return ResultSuccess(); + } + + static Result End(Report *report) { + return ResultSuccess(); + } + + template + static Result AddField(Report *report, FieldId field_id, T value) { + return AddIdValuePair(report, field_id, value); + } + + template + static Result AddField(Report *report, FieldId field_id, T *arr, u32 arr_size) { + return AddIdValueArray(report, field_id, arr, arr_size); + } + + static Result AddField(Report *report, FieldId field_id, bool value) { + R_TRY(AddId(report, field_id)); + R_TRY(report->Write(static_cast(value ? ValueTypeTag::True : ValueTypeTag::False))); + return ResultSuccess(); + } + + static Result AddField(Report *report, FieldId field_id, char *str, u32 len) { + R_TRY(AddId(report, field_id)); + + const u32 str_len = str != nullptr ? static_cast(strnlen(str, len)) : 0; + + if (str_len < ElementSize_32) { + R_TRY(report->Write(static_cast(static_cast(ValueTypeTag::FixStr) | str_len))); + } else if (str_len < ElementSize_256) { + R_TRY(report->Write(static_cast(ValueTypeTag::Str8))); + R_TRY(report->Write(static_cast(str_len))); + } else { + R_UNLESS(str_len < ElementSize_16384, erpt::ResultFormatterError()); + R_TRY(report->Write(static_cast(ValueTypeTag::Str16))); + + u16 be_str_len; + util::StoreBigEndian(std::addressof(be_str_len), static_cast(str_len)); + R_TRY(report->Write(be_str_len)); + } + + R_TRY(report->Write(str, str_len)); + + return ResultSuccess(); + } + + static Result AddField(Report *report, FieldId field_id, u8 *bin, u32 len) { + R_TRY(AddId(report, field_id)); + + if (len < ElementSize_256) { + R_TRY(report->Write(static_cast(ValueTypeTag::Bin8))); + R_TRY(report->Write(static_cast(len))); + } else { + R_UNLESS(len < ElementSize_16384, erpt::ResultFormatterError()); + R_TRY(report->Write(static_cast(ValueTypeTag::Bin16))); + + u16 be_len; + util::StoreBigEndian(std::addressof(be_len), static_cast(len)); + R_TRY(report->Write(be_len)); + } + + R_TRY(report->Write(bin, len)); + + return ResultSuccess(); + } + }; + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_journal.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_journal.cpp new file mode 100644 index 000000000..cfbb7d072 --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_journal.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "erpt_srv_journal.hpp" + +namespace ams::erpt::srv { + + void Journal::CleanupAttachments() { + return JournalForAttachments::CleanupAttachments(); + } + + void Journal::CleanupReports() { + return JournalForReports::CleanupReports(); + } + + Result Journal::Commit() { + /* Open the stream. */ + Stream stream; + R_TRY(stream.OpenStream(JournalFileName, StreamMode_Write, JournalStreamBufferSize)); + + /* Commit the reports. */ + R_TRY(JournalForReports::CommitJournal(std::addressof(stream))); + + /* Commit the meta. */ + R_TRY(JournalForMeta::CommitJournal(std::addressof(stream))); + + /* Commit the attachments. */ + R_TRY(JournalForAttachments::CommitJournal(std::addressof(stream))); + + /* Close and commit the stream. */ + stream.CloseStream(); + stream.CommitStream(); + + return ResultSuccess(); + } + + Result Journal::Delete(ReportId report_id) { + return JournalForReports::DeleteReport(report_id); + } + + Result Journal::GetAttachmentList(AttachmentList *out, ReportId report_id) { + return JournalForAttachments::GetAttachmentList(out, report_id); + } + + util::Uuid Journal::GetJournalId() { + return JournalForMeta::GetJournalId(); + } + + s64 Journal::GetMaxReportSize() { + return JournalForReports::GetMaxReportSize(); + } + + Result Journal::GetReportList(ReportList *out, ReportType type_filter) { + return JournalForReports::GetReportList(out, type_filter); + } + + u32 Journal::GetStoredReportCount(ReportType type) { + return JournalForReports::GetStoredReportCount(type); + } + + u32 Journal::GetTransmittedCount(ReportType type) { + return JournalForMeta::GetTransmittedCount(type); + } + + u32 Journal::GetUntransmittedCount(ReportType type) { + return JournalForMeta::GetUntransmittedCount(type); + } + + u32 Journal::GetUsedStorage() { + return JournalForReports::GetUsedStorage() + JournalForAttachments::GetUsedStorage(); + } + + Result Journal::Restore() { + /* Open the stream. */ + Stream stream; + R_TRY(stream.OpenStream(JournalFileName, StreamMode_Read, JournalStreamBufferSize)); + + /* Restore the reports. */ + R_TRY(JournalForReports::RestoreJournal(std::addressof(stream))); + + /* Restore the meta. */ + R_TRY(JournalForMeta::RestoreJournal(std::addressof(stream))); + + /* Restore the attachments. */ + R_TRY(JournalForAttachments::RestoreJournal(std::addressof(stream))); + + return ResultSuccess(); + } + + JournalRecord *Journal::Retrieve(ReportId report_id) { + return JournalForReports::RetrieveRecord(report_id); + } + + JournalRecord *Journal::Retrieve(AttachmentId attachment_id) { + return JournalForAttachments::RetrieveRecord(attachment_id); + } + + Result Journal::Store(JournalRecord *record) { + return JournalForReports::StoreRecord(record); + } + + Result Journal::Store(JournalRecord *record) { + return JournalForAttachments::StoreRecord(record); + } + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_journal.hpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_journal.hpp new file mode 100644 index 000000000..03f4e7a3c --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_journal.hpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "erpt_srv_allocator.hpp" +#include "erpt_srv_ref_count.hpp" +#include "erpt_srv_journal_record.hpp" +#include "erpt_srv_stream.hpp" + +namespace ams::erpt::srv { + + constexpr inline s32 JournalVersion = 1; + + constexpr inline u32 JournalStreamBufferSize = 4_KB; + + struct JournalMeta { + s32 version; + u32 transmitted_count[ReportType_Count]; + u32 untransmitted_count[ReportType_Count]; + util::Uuid journal_id; + u32 reserved[4]; + }; + static_assert(sizeof(JournalMeta) == 0x34); + + class JournalForMeta { + private: + static JournalMeta s_journal_meta; + public: + static void InitializeJournal(); + static Result CommitJournal(Stream *stream); + static Result RestoreJournal(Stream *stream); + static u32 GetTransmittedCount(ReportType type); + static u32 GetUntransmittedCount(ReportType type); + static void IncrementCount(bool transmitted, ReportType type); + static util::Uuid GetJournalId(); + }; + + class JournalForReports { + private: + using RecordListType = util::IntrusiveListBaseTraits>::ListType; + static RecordListType s_record_list; + static u32 s_record_count; + static u32 s_record_count_by_type[ReportType_Count]; + static u32 s_used_storage; + private: + static void EraseReportImpl(JournalRecord *record, bool increment_count, bool force_delete_attachments); + public: + static void CleanupReports(); + static Result CommitJournal(Stream *stream); + static Result DeleteReport(ReportId report_id); + static Result DeleteReportWithAttachments(); + static s64 GetMaxReportSize(); + static Result GetReportList(ReportList *out, ReportType type_filter); + static u32 GetStoredReportCount(ReportType type); + static u32 GetUsedStorage(); + static Result RestoreJournal(Stream *stream); + + static JournalRecord *RetrieveRecord(ReportId report_id); + static Result StoreRecord(JournalRecord *record); + }; + + class JournalForAttachments { + private: + using AttachmentListType = util::IntrusiveListBaseTraits>::ListType; + static AttachmentListType s_attachment_list; + static u32 s_attachment_count; + static u32 s_used_storage; + public: + static void CleanupAttachments(); + static Result CommitJournal(Stream *stream); + static Result DeleteAttachments(ReportId report_id); + static Result GetAttachmentList(AttachmentList *out, ReportId report_id); + static u32 GetUsedStorage(); + static Result RestoreJournal(Stream *stream); + + static JournalRecord *RetrieveRecord(AttachmentId attachment_id); + static Result SetOwner(AttachmentId attachment_id, ReportId report_id); + static Result StoreRecord(JournalRecord *record); + + static Result SubmitAttachment(AttachmentId *out, char *name, const u8 *data, u32 data_size); + }; + + class Journal { + public: + static void CleanupAttachments(); + static void CleanupReports(); + static Result Commit(); + static Result Delete(ReportId report_id); + static Result GetAttachmentList(AttachmentList *out, ReportId report_id); + static util::Uuid GetJournalId(); + static s64 GetMaxReportSize(); + static Result GetReportList(ReportList *out, ReportType type_filter); + static u32 GetStoredReportCount(ReportType type); + static u32 GetTransmittedCount(ReportType type); + static u32 GetUntransmittedCount(ReportType type); + static u32 GetUsedStorage(); + static Result Restore(); + + static JournalRecord *Retrieve(ReportId report_id); + static JournalRecord *Retrieve(AttachmentId attachment_id); + + static Result Store(JournalRecord *record); + static Result Store(JournalRecord *record); + }; + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_journal_for_attachments.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_journal_for_attachments.cpp new file mode 100644 index 000000000..66060f3b5 --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_journal_for_attachments.cpp @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "erpt_srv_journal.hpp" +#include "erpt_srv_attachment.hpp" + +namespace ams::erpt::srv { + + util::IntrusiveListBaseTraits>::ListType JournalForAttachments::s_attachment_list; + u32 JournalForAttachments::s_attachment_count = 0; + u32 JournalForAttachments::s_used_storage = 0; + + namespace { + + constexpr inline u32 AttachmentUsedStorageMax = 4_MB; + + } + + void JournalForAttachments::CleanupAttachments() { + for (auto it = s_attachment_list.begin(); it != s_attachment_list.end(); /* ... */) { + auto *record = std::addressof(*it); + it = s_attachment_list.erase(s_attachment_list.iterator_to(*record)); + if (record->RemoveReference()) { + Stream::DeleteStream(Attachment::FileName(record->info.attachment_id).name); + delete record; + } + } + AMS_ASSERT(s_attachment_list.empty()); + + s_attachment_count = 0; + s_used_storage = 0; + + } + + Result JournalForAttachments::CommitJournal(Stream *stream) { + R_TRY(stream->WriteStream(reinterpret_cast(std::addressof(s_attachment_count)), sizeof(s_attachment_count))); + for (auto it = s_attachment_list.crbegin(); it != s_attachment_list.crend(); it++) { + R_TRY(stream->WriteStream(reinterpret_cast(std::addressof(it->info)), sizeof(it->info))); + } + return ResultSuccess(); + } + + Result JournalForAttachments::DeleteAttachments(ReportId report_id) { + for (auto it = s_attachment_list.begin(); it != s_attachment_list.end(); /* ... */) { + auto *record = std::addressof(*it); + if (record->info.owner_report_id == report_id) { + /* Erase from the list. */ + it = s_attachment_list.erase(s_attachment_list.iterator_to(*record)); + + /* Update storage tracking counts. */ + --s_attachment_count; + s_used_storage -= static_cast(record->info.attachment_size); + + /* Delete the object, if we should. */ + if (record->RemoveReference()) { + Stream::DeleteStream(Attachment::FileName(record->info.attachment_id).name); + delete record; + } + } else { + /* Not attached, just advance. */ + it++; + } + } + return ResultSuccess(); + } + + Result JournalForAttachments::GetAttachmentList(AttachmentList *out, ReportId report_id) { + u32 count = 0; + for (auto it = s_attachment_list.cbegin(); it != s_attachment_list.cend() && count < util::size(out->attachments); it++) { + if (report_id == it->info.owner_report_id) { + out->attachments[count++] = it->info; + } + } + out->attachment_count = count; + return ResultSuccess(); + } + + u32 JournalForAttachments::GetUsedStorage() { + return s_used_storage; + } + + Result JournalForAttachments::RestoreJournal(Stream *stream) { + /* Clear the used storage. */ + s_used_storage = 0; + + /* Read the count from storage. */ + u32 read_size; + u32 count; + R_TRY(stream->ReadStream(std::addressof(read_size), reinterpret_cast(std::addressof(count)), sizeof(count))); + + R_UNLESS(read_size == sizeof(count), erpt::ResultCorruptJournal()); + R_UNLESS(count <= AttachmentCountMax, erpt::ResultCorruptJournal()); + + /* If we fail in the middle of reading reports, we want to do cleanup. */ + auto cleanup_guard = SCOPE_GUARD { CleanupAttachments(); }; + + AttachmentInfo info; + for (u32 i = 0; i < count; i++) { + R_TRY(stream->ReadStream(std::addressof(read_size), reinterpret_cast(std::addressof(info)), sizeof(info))); + + R_UNLESS(read_size == sizeof(info), erpt::ResultCorruptJournal()); + + auto *record = new JournalRecord(info); + R_UNLESS(record != nullptr, erpt::ResultOutOfMemory()); + + auto record_guard = SCOPE_GUARD { delete record; }; + + if (R_FAILED(Stream::GetStreamSize(std::addressof(record->info.attachment_size), Attachment::FileName(record->info.attachment_id).name))) { + continue; + } + + if (record->info.flags.Test() && JournalForReports::RetrieveRecord(record->info.owner_report_id) != nullptr) { + /* NOTE: Nintendo does not check the result of storing the new record... */ + record_guard.Cancel(); + StoreRecord(record); + } else { + /* If the attachment has no owner (or we deleted the report), delete the file associated with it. */ + Stream::DeleteStream(Attachment::FileName(record->info.attachment_id).name); + } + } + + cleanup_guard.Cancel(); + return ResultSuccess(); + } + + JournalRecord *JournalForAttachments::RetrieveRecord(AttachmentId attachment_id) { + for (auto it = s_attachment_list.begin(); it != s_attachment_list.end(); it++) { + return std::addressof(*it); + } + return nullptr; + } + + Result JournalForAttachments::SetOwner(AttachmentId attachment_id, ReportId report_id) { + for (auto it = s_attachment_list.begin(); it != s_attachment_list.end(); it++) { + auto *record = std::addressof(*it); + if (record->info.attachment_id == attachment_id) { + R_UNLESS(!record->info.flags.Test(), erpt::ResultAlreadyOwned()); + + record->info.owner_report_id = report_id; + record->info.flags.Set(); + return ResultSuccess(); + } + } + return erpt::ResultInvalidArgument(); + } + + Result JournalForAttachments::StoreRecord(JournalRecord *record) { + /* Check if the record already exists. */ + for (auto it = s_attachment_list.begin(); it != s_attachment_list.end(); it++) { + R_UNLESS(it->info.attachment_id != record->info.attachment_id, erpt::ResultAlreadyExists()); + } + + /* Add a reference to the new record. */ + record->AddReference(); + + /* Push the record into the list. */ + s_attachment_list.push_front(*record); + s_attachment_count++; + s_used_storage += static_cast(record->info.attachment_size); + + return ResultSuccess(); + } + + Result JournalForAttachments::SubmitAttachment(AttachmentId *out, char *name, const u8 *data, u32 data_size) { + R_UNLESS(data_size > 0, erpt::ResultInvalidArgument()); + R_UNLESS(data_size < AttachmentSizeMax, erpt::ResultInvalidArgument()); + + const auto name_len = std::strlen(name); + R_UNLESS(name_len < AttachmentNameSizeMax, erpt::ResultInvalidArgument()); + + /* Ensure that we have free space. */ + while (s_used_storage > AttachmentUsedStorageMax) { + R_TRY(JournalForReports::DeleteReportWithAttachments()); + } + + AttachmentInfo info; + info.attachment_id.uuid = util::GenerateUuid(); + info.flags = erpt::srv::MakeNoAttachmentFlags(); + info.attachment_size = data_size; + util::Strlcpy(info.attachment_name, name, sizeof(info.attachment_name)); + + auto *record = new JournalRecord(info); + R_UNLESS(record != nullptr, erpt::ResultOutOfMemory()); + + record->AddReference(); + ON_SCOPE_EXIT { + if (record->RemoveReference()) { + delete record; + } + }; + + { + auto attachment = std::make_unique(record); + R_UNLESS(attachment != nullptr, erpt::ResultOutOfMemory()); + + R_TRY(attachment->Open(AttachmentOpenType_Create)); + ON_SCOPE_EXIT { attachment->Close(); }; + + R_TRY(attachment->Write(data, data_size)); + R_TRY(StoreRecord(record)); + } + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_journal_for_meta.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_journal_for_meta.cpp new file mode 100644 index 000000000..2a5c1f784 --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_journal_for_meta.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "erpt_srv_journal.hpp" + +namespace ams::erpt::srv { + + JournalMeta JournalForMeta::s_journal_meta; + + void JournalForMeta::InitializeJournal() { + std::memset(std::addressof(s_journal_meta), 0, sizeof(s_journal_meta)); + s_journal_meta.journal_id = util::GenerateUuid(); + s_journal_meta.version = JournalVersion; + } + + Result JournalForMeta::CommitJournal(Stream *stream) { + return stream->WriteStream(reinterpret_cast(std::addressof(s_journal_meta)), sizeof(s_journal_meta)); + } + + Result JournalForMeta::RestoreJournal(Stream *stream) { + u32 size; + if (R_FAILED(stream->ReadStream(std::addressof(size), reinterpret_cast(std::addressof(s_journal_meta)), sizeof(s_journal_meta))) || size != sizeof(s_journal_meta)) { + InitializeJournal(); + } + return ResultSuccess(); + } + + u32 JournalForMeta::GetTransmittedCount(ReportType type) { + if (ReportType_Start <= type && type < ReportType_End) { + return s_journal_meta.transmitted_count[type]; + } else { + return 0; + } + } + + u32 JournalForMeta::GetUntransmittedCount(ReportType type) { + if (ReportType_Start <= type && type < ReportType_End) { + return s_journal_meta.untransmitted_count[type]; + } else { + return 0; + } + } + + void JournalForMeta::IncrementCount(bool transmitted, ReportType type) { + if (ReportType_Start <= type && type < ReportType_End) { + if (transmitted) { + s_journal_meta.transmitted_count[type]++; + } else { + s_journal_meta.untransmitted_count[type]++; + } + } + } + + util::Uuid JournalForMeta::GetJournalId() { + return s_journal_meta.journal_id; + } + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_journal_for_reports.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_journal_for_reports.cpp new file mode 100644 index 000000000..5db553eb2 --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_journal_for_reports.cpp @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "erpt_srv_journal.hpp" +#include "erpt_srv_report.hpp" + +namespace ams::erpt::srv { + + util::IntrusiveListBaseTraits>::ListType JournalForReports::s_record_list; + u32 JournalForReports::s_record_count = 0; + u32 JournalForReports::s_record_count_by_type[ReportType_Count] = {}; + u32 JournalForReports::s_used_storage = 0; + + void JournalForReports::CleanupReports() { + for (auto it = s_record_list.begin(); it != s_record_list.end(); /* ... */) { + auto *record = std::addressof(*it); + it = s_record_list.erase(s_record_list.iterator_to(*record)); + if (record->RemoveReference()) { + Stream::DeleteStream(Report::FileName(record->info.id, false).name); + delete record; + } + } + AMS_ASSERT(s_record_list.empty()); + + s_record_count = 0; + s_used_storage = 0; + + std::memset(s_record_count_by_type, 0, sizeof(s_record_count_by_type)); + } + + Result JournalForReports::CommitJournal(Stream *stream) { + R_TRY(stream->WriteStream(reinterpret_cast(std::addressof(s_record_count)), sizeof(s_record_count))); + for (auto it = s_record_list.crbegin(); it != s_record_list.crend(); it++) { + R_TRY(stream->WriteStream(reinterpret_cast(std::addressof(it->info)), sizeof(it->info))); + } + return ResultSuccess(); + } + + void JournalForReports::EraseReportImpl(JournalRecord *record, bool increment_count, bool force_delete_attachments) { + /* Erase from the list. */ + s_record_list.erase(s_record_list.iterator_to(*record)); + + /* Update storage tracking counts. */ + --s_record_count; + --s_record_count_by_type[record->info.type]; + s_used_storage -= static_cast(record->info.report_size); + + /* If we should increment count, do so. */ + if (increment_count) { + JournalForMeta::IncrementCount(record->info.flags.Test(), record->info.type); + } + + /* Delete any attachments. */ + if (force_delete_attachments || record->info.flags.Test()) { + JournalForAttachments::DeleteAttachments(record->info.id); + } + + /* Delete the object, if we should. */ + if (record->RemoveReference()) { + Stream::DeleteStream(Report::FileName(record->info.id, false).name); + delete record; + } + } + + Result JournalForReports::DeleteReport(ReportId report_id) { + for (auto it = s_record_list.begin(); it != s_record_list.end(); it++) { + auto *record = std::addressof(*it); + if (record->info.id == report_id) { + EraseReportImpl(record, false, false); + return ResultSuccess(); + } + } + return erpt::ResultInvalidArgument(); + } + + Result JournalForReports::DeleteReportWithAttachments() { + for (auto it = s_record_list.rbegin(); it != s_record_list.rend(); it++) { + auto *record = std::addressof(*it); + if (record->info.flags.Test()) { + EraseReportImpl(record, true, true); + return ResultSuccess(); + } + } + return erpt::ResultNotFound(); + } + + s64 JournalForReports::GetMaxReportSize() { + s64 max_size; + for (auto it = s_record_list.begin(); it != s_record_list.end(); it++) { + max_size = std::max(max_size, it->info.report_size); + } + return max_size; + } + + Result JournalForReports::GetReportList(ReportList *out, ReportType type_filter) { + u32 count = 0; + for (auto it = s_record_list.cbegin(); it != s_record_list.cend() && count < util::size(out->reports); it++) { + if (type_filter == ReportType_Any || type_filter == it->info.type) { + out->reports[count++] = it->info; + } + } + out->report_count = count; + return ResultSuccess(); + } + + u32 JournalForReports::GetStoredReportCount(ReportType type) { + if (ReportType_Start <= type && type < ReportType_End) { + return s_record_count_by_type[type]; + } else { + return 0; + } + } + + u32 JournalForReports::GetUsedStorage() { + return s_used_storage; + } + + Result JournalForReports::RestoreJournal(Stream *stream) { + /* Clear the used storage. */ + s_used_storage = 0; + + /* Read the count from storage. */ + u32 read_size; + u32 count; + R_TRY(stream->ReadStream(std::addressof(read_size), reinterpret_cast(std::addressof(count)), sizeof(count))); + + R_UNLESS(read_size == sizeof(count), erpt::ResultCorruptJournal()); + R_UNLESS(count <= ReportCountMax, erpt::ResultCorruptJournal()); + + /* If we fail in the middle of reading reports, we want to do cleanup. */ + auto cleanup_guard = SCOPE_GUARD { CleanupReports(); }; + + ReportInfo info; + for (u32 i = 0; i < count; i++) { + R_TRY(stream->ReadStream(std::addressof(read_size), reinterpret_cast(std::addressof(info)), sizeof(info))); + + R_UNLESS(read_size == sizeof(info), erpt::ResultCorruptJournal()); + R_UNLESS(ReportType_Start <= info.type, erpt::ResultCorruptJournal()); + R_UNLESS(info.type < ReportType_End, erpt::ResultCorruptJournal()); + + auto *record = new JournalRecord(info); + R_UNLESS(record != nullptr, erpt::ResultOutOfMemory()); + + /* NOTE: Nintendo does not ensure that the newly allocated record does not leak in the failure case. */ + /* We will ensure it is freed if we early error. */ + auto record_guard = SCOPE_GUARD { delete record; }; + + if (record->info.report_size == 0) { + R_UNLESS(R_SUCCEEDED(Stream::GetStreamSize(std::addressof(record->info.report_size), Report::FileName(record->info.id, false).name)), erpt::ResultCorruptJournal()); + } + + record_guard.Cancel(); + + /* NOTE: Nintendo does not check the result of storing the new record... */ + StoreRecord(record); + } + + cleanup_guard.Cancel(); + return ResultSuccess(); + } + + JournalRecord *JournalForReports::RetrieveRecord(ReportId report_id) { + for (auto it = s_record_list.begin(); it != s_record_list.end(); it++) { + return std::addressof(*it); + } + return nullptr; + } + + Result JournalForReports::StoreRecord(JournalRecord *record) { + /* Check if the record already exists. */ + for (auto it = s_record_list.begin(); it != s_record_list.end(); it++) { + R_UNLESS(it->info.id != record->info.id, erpt::ResultAlreadyExists()); + } + + /* Delete an older report if we need to. */ + if (s_record_count >= ReportCountMax) { + /* Nintendo deletes the oldest report from the type with the most reports. */ + /* This is an approximation of FIFO. */ + ReportType most_used_type = record->info.type; + u32 most_used_count = s_record_count_by_type[most_used_type]; + + for (int i = ReportType_Start; i < ReportType_End; i++) { + if (s_record_count_by_type[i] > most_used_count) { + most_used_type = static_cast(i); + most_used_count = s_record_count_by_type[i]; + } + } + + for (auto it = s_record_list.rbegin(); it != s_record_list.rend(); it++) { + if (it->info.type != most_used_type) { + continue; + } + + EraseReportImpl(std::addressof(*it), true, false); + break; + } + } + AMS_ASSERT(s_record_count < ReportCountMax); + + /* Add a reference to the new record. */ + record->AddReference(); + + /* Push the record into the list. */ + s_record_list.push_front(*record); + s_record_count++; + s_record_count_by_type[record->info.type]++; + s_used_storage += static_cast(record->info.report_size); + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_journal_record.hpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_journal_record.hpp new file mode 100644 index 000000000..55f36c3e9 --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_journal_record.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "erpt_srv_allocator.hpp" +#include "erpt_srv_ref_count.hpp" + +namespace ams::erpt::srv { + + template + class JournalRecord : public Allocator, public RefCount, public util::IntrusiveListBaseNode> { + public: + Info info; + + JournalRecord() { + std::memset(std::addressof(this->info), 0, sizeof(this->info)); + } + + explicit JournalRecord(Info info) : info(info) { /* ... */ } + + }; + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_keys.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_keys.cpp new file mode 100644 index 000000000..69183c367 --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_keys.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "erpt_srv_keys.hpp" + +namespace ams::erpt::srv { + + namespace { + + constexpr const u8 PublicKeyModulusProduction[] = { + 0x00, + 0xAB, 0xB6, 0x6C, 0x43, 0x86, 0xDF, 0x52, 0x5F, 0xB9, 0xD3, 0x61, 0xEB, 0xFB, 0x16, 0x2B, 0x44, + 0xE1, 0x1F, 0xDD, 0x81, 0xFA, 0x20, 0x48, 0x7B, 0xDB, 0x17, 0x9F, 0x9A, 0x92, 0x1E, 0x0A, 0x80, + 0xD1, 0x1A, 0x4F, 0xD7, 0x49, 0xFE, 0xBA, 0x65, 0xE4, 0x61, 0x08, 0x26, 0x43, 0x7B, 0x4A, 0x16, + 0x59, 0x60, 0xD1, 0xE0, 0x42, 0x0A, 0x26, 0x54, 0xC4, 0xC7, 0x2A, 0xE3, 0x17, 0x1F, 0x8E, 0x35, + 0x79, 0xC0, 0x1B, 0xA1, 0xF8, 0x6F, 0x5C, 0xDC, 0x05, 0x2D, 0x90, 0x75, 0xD5, 0x98, 0x7E, 0x5A, + 0x07, 0x3F, 0x4E, 0x78, 0xF1, 0x69, 0x2F, 0xE9, 0x2E, 0x50, 0x01, 0x9F, 0xCE, 0x35, 0xC8, 0x4D, + 0x65, 0x23, 0xA8, 0x9F, 0xC3, 0x3C, 0x4A, 0xF2, 0x29, 0x4D, 0x10, 0x03, 0x1C, 0xB4, 0x0E, 0x64, + 0xB6, 0xDD, 0xB2, 0x74, 0xE3, 0x32, 0x84, 0x25, 0x99, 0xEA, 0xE1, 0x6C, 0x78, 0x24, 0xF2, 0xB0, + 0xD2, 0x2C, 0xA5, 0x1A, 0x70, 0xA6, 0x49, 0x08, 0x73, 0x8A, 0x74, 0x3A, 0x12, 0x0E, 0x1B, 0x68, + 0xD1, 0x6A, 0x6C, 0x3F, 0x2C, 0x2C, 0x53, 0xD5, 0xCE, 0x5A, 0x07, 0xA2, 0xB9, 0x2E, 0x0A, 0x77, + 0x51, 0x4B, 0xD2, 0x8E, 0x4F, 0xA3, 0xA8, 0x56, 0x99, 0x6F, 0x63, 0xBC, 0x23, 0x04, 0x6E, 0x71, + 0x57, 0x7C, 0xFD, 0x84, 0xA7, 0xF8, 0x8D, 0x7F, 0xD6, 0xA0, 0x6E, 0x92, 0xBC, 0xCC, 0x28, 0x82, + 0x60, 0xE9, 0x78, 0xC1, 0x31, 0x82, 0x4F, 0xF8, 0xC5, 0xDB, 0xB6, 0x6B, 0xF9, 0x62, 0x95, 0xD3, + 0xC8, 0x63, 0x59, 0x53, 0x3F, 0x82, 0xEB, 0x06, 0xA7, 0xB8, 0x55, 0xEC, 0x9E, 0x33, 0x04, 0xCF, + 0x5E, 0x42, 0x32, 0x09, 0x26, 0xFF, 0xB4, 0x5E, 0xBD, 0xD7, 0xA8, 0x6B, 0x2C, 0xF5, 0x68, 0x86, + 0xCD, 0x8A, 0x13, 0xF3, 0x1C, 0x5F, 0xE6, 0x4F, 0xFC, 0xD1, 0x07, 0x28, 0x5C, 0x2D, 0xA7, 0xF7 + }; + + constexpr const u8 PublicKeyModulusDevelopment[] = { + 0x00, + 0xAE, 0x7D, 0x6C, 0xD0, 0xC3, 0x13, 0x61, 0x01, 0x9D, 0x1B, 0x55, 0xA0, 0xE5, 0xF4, 0x3D, 0x56, + 0x7D, 0xCA, 0x0E, 0x49, 0xFB, 0x82, 0x08, 0x06, 0x33, 0xB6, 0x37, 0xB3, 0x4A, 0x3F, 0x57, 0x39, + 0x05, 0x84, 0x18, 0x3D, 0x82, 0xD8, 0x8F, 0xBC, 0xF3, 0xE1, 0x66, 0xEE, 0xD2, 0x80, 0x43, 0xF8, + 0xA7, 0xB7, 0x5E, 0x5B, 0x5C, 0xF9, 0x9D, 0x7F, 0xE9, 0x6C, 0x93, 0x8A, 0x65, 0xBB, 0xD1, 0xDD, + 0x56, 0xFF, 0x7C, 0x9D, 0x24, 0x66, 0x09, 0x84, 0x21, 0x2C, 0x7F, 0x0A, 0xB8, 0x31, 0x42, 0x29, + 0xE6, 0xD3, 0x20, 0x76, 0xA1, 0x1F, 0x7E, 0x59, 0x5B, 0x7C, 0xF6, 0xC6, 0x02, 0xDB, 0xC9, 0x1B, + 0xB9, 0x24, 0x99, 0xAD, 0x0F, 0x7B, 0x0D, 0x8E, 0x7E, 0x01, 0xFE, 0x95, 0xCE, 0x9B, 0xB5, 0x09, + 0xC5, 0xF5, 0xA5, 0x6A, 0x82, 0xF6, 0x57, 0xF8, 0x06, 0x72, 0xAE, 0x73, 0x71, 0xD1, 0x09, 0x2B, + 0xE2, 0x84, 0x0D, 0x66, 0x39, 0xB6, 0x21, 0x8B, 0x35, 0xE4, 0xDF, 0x90, 0x36, 0xE1, 0x3F, 0xC0, + 0x9F, 0xF8, 0x85, 0x03, 0xD6, 0xCA, 0xBB, 0x1A, 0x62, 0x2D, 0xE5, 0x03, 0xF6, 0x47, 0x00, 0x6E, + 0x98, 0x5A, 0x1C, 0x51, 0x94, 0x47, 0xF6, 0x83, 0x0C, 0x25, 0xBD, 0xBE, 0xBD, 0x6A, 0x35, 0xC0, + 0xAB, 0x65, 0xF8, 0x01, 0xF4, 0xC3, 0x2A, 0xA3, 0xBC, 0xD7, 0xD9, 0xF7, 0x2A, 0x98, 0x27, 0xE1, + 0x3F, 0x9A, 0xCF, 0xDF, 0xB1, 0x30, 0x82, 0xA4, 0xAA, 0x78, 0xCA, 0xC8, 0xB8, 0x34, 0xFA, 0xA7, + 0x75, 0x23, 0xC9, 0x9C, 0x11, 0x68, 0x7E, 0x0F, 0x80, 0x8F, 0x90, 0xA6, 0xDE, 0x2B, 0x47, 0x5B, + 0x94, 0x6F, 0xB9, 0x67, 0x4C, 0xC1, 0xAE, 0x50, 0x8F, 0xD8, 0xE3, 0xD1, 0xF2, 0x92, 0x54, 0x4C, + 0x25, 0x02, 0x5B, 0x31, 0x65, 0x5E, 0x41, 0x81, 0x34, 0xF4, 0xF1, 0x34, 0xE7, 0x64, 0x7A, 0xC1 + }; + + constexpr const u8 PublicKeyExponent[] = { + 0x01, 0x00, 0x01 + }; + + bool IsProductionModeImpl() { + bool is_prod = true; + if (settings::fwdbg::GetSettingsItemValue(std::addressof(is_prod), sizeof(is_prod), "erpt", "production_mode") != sizeof(is_prod)) { + return true; + } + return is_prod; + } + + bool IsProductionMode() { + static bool s_is_prod_mode = IsProductionModeImpl(); + return s_is_prod_mode; + } + + } + + const u8 *GetPublicKeyModulus() { + return IsProductionMode() ? PublicKeyModulusProduction : PublicKeyModulusDevelopment; + } + + size_t GetPublicKeyModulusSize() { + return IsProductionMode() ? sizeof(PublicKeyModulusProduction) : sizeof(PublicKeyModulusDevelopment); + } + + const u8 *GetPublicKeyExponent() { + return PublicKeyExponent; + } + + size_t GetPublicKeyExponentSize() { + return sizeof(PublicKeyExponent); + } + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_keys.hpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_keys.hpp new file mode 100644 index 000000000..1c5534571 --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_keys.hpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::erpt::srv { + + const u8 *GetPublicKeyModulus(); + size_t GetPublicKeyModulusSize(); + + const u8 *GetPublicKeyExponent(); + size_t GetPublicKeyExponentSize(); + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_main.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_main.cpp new file mode 100644 index 000000000..655558bea --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_main.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "erpt_srv_allocator.hpp" +#include "erpt_srv_context_record.hpp" +#include "erpt_srv_context.hpp" +#include "erpt_srv_reporter.hpp" +#include "erpt_srv_journal.hpp" +#include "erpt_srv_service.hpp" + +namespace ams::erpt::srv { + + lmem::HeapHandle g_heap_handle; + + namespace { + + constexpr fs::SystemSaveDataId SystemSaveDataId = 0x80000000000000D1; + constexpr u32 SystemSaveDataFlags = fs::SaveDataFlags_KeepAfterResettingSystemSaveDataWithoutUserSaveData; + constexpr s64 SystemSaveDataSize = 11_MB; + constexpr s64 SystemSaveDataJournalSize = 2720_KB; + + Result ExtendSystemSaveData() { + s64 cur_journal_size; + s64 cur_savedata_size; + + R_TRY(fs::GetSaveDataJournalSize(std::addressof(cur_journal_size), SystemSaveDataId)); + R_TRY(fs::GetSaveDataAvailableSize(std::addressof(cur_savedata_size), SystemSaveDataId)); + + if (cur_journal_size < SystemSaveDataJournalSize || cur_savedata_size < SystemSaveDataSize) { + if (hos::GetVersion() >= hos::Version_300) { + R_TRY(fs::ExtendSaveData(fs::SaveDataSpaceId::System, SystemSaveDataId, SystemSaveDataSize, SystemSaveDataJournalSize)); + } + } + + return ResultSuccess(); + } + + Result MountSystemSaveData() { + fs::DisableAutoSaveDataCreation(); + + /* Extend the system save data. */ + /* NOTE: Nintendo does not check result of this. */ + ExtendSystemSaveData(); + + R_TRY_CATCH(fs::MountSystemSaveData(ReportStoragePath, SystemSaveDataId)) { + R_CATCH(fs::ResultTargetNotFound) { + R_TRY(fs::CreateSystemSaveData(SystemSaveDataId, SystemSaveDataSize, SystemSaveDataJournalSize, SystemSaveDataFlags)); + R_TRY(fs::MountSystemSaveData(ReportStoragePath, SystemSaveDataId)); + } + } R_END_TRY_CATCH; + + return ResultSuccess(); + } + + } + + Result Initialize(u8 *mem, size_t mem_size) { + R_ABORT_UNLESS(time::Initialize()); + + g_heap_handle = lmem::CreateExpHeap(mem, mem_size, lmem::CreateOption_ThreadSafe); + AMS_ABORT_UNLESS(g_heap_handle != nullptr); + + fs::SetAllocator(Allocate, DeallocateWithSize); + + R_ABORT_UNLESS(fs::MountSdCardErrorReportDirectoryForAtmosphere(ReportOnSdStoragePath)); + + R_ABORT_UNLESS(MountSystemSaveData()); + + for (auto i = 0; i < CategoryId_Count; i++) { + Context *ctx = new Context(static_cast(i), 1); + AMS_ABORT_UNLESS(ctx != nullptr); + } + + Journal::Restore(); + + return ResultSuccess(); + } + + Result InitializeAndStartService() { + return InitializeService(); + } + + Result SetSerialNumberAndOsVersion(const char *sn, u32 sn_len, const char *os, u32 os_len, const char *os_priv, u32 os_priv_len) { + return Reporter::SetSerialNumberAndOsVersion(sn, sn_len, os, os_len, os_priv, os_priv_len); + } + + Result SetProductModel(const char *model, u32 model_len) { + /* NOTE: Nintendo does not check that this allocation succeeds. */ + auto *record = new ContextRecord(CategoryId_ProductModelInfo); + R_UNLESS(record != nullptr, erpt::ResultOutOfMemory()); + + R_TRY(record->Add(FieldId_ProductModel, model, model_len)); + R_TRY(Context::SubmitContextRecord(record)); + + return ResultSuccess(); + } + + Result SetRegionSetting(const char *region, u32 region_len) { + /* NOTE: Nintendo does not check that this allocation succeeds. */ + auto *record = new ContextRecord(CategoryId_RegionSettingInfo); + R_UNLESS(record != nullptr, erpt::ResultOutOfMemory()); + + R_TRY(record->Add(FieldId_RegionSetting, region, region_len)); + R_TRY(Context::SubmitContextRecord(record)); + + return ResultSuccess(); + } + + Result SetRedirectNewReportsToSdCard(bool redirect) { + Reporter::SetRedirectNewReportsToSdCard(redirect); + return ResultSuccess(); + } + + void Wait() { + return WaitService(); + } + + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_manager_impl.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_manager_impl.cpp new file mode 100644 index 000000000..dc9e990d0 --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_manager_impl.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "erpt_srv_manager_impl.hpp" +#include "erpt_srv_journal.hpp" + +namespace ams::erpt::srv { + + namespace { + + using ManagerList = util::IntrusiveListBaseTraits::ListType; + + ManagerList g_manager_list; + + } + + ManagerImpl::ManagerImpl() : system_event(os::EventClearMode_AutoClear, true) { + g_manager_list.push_front(*this); + } + + ManagerImpl::~ManagerImpl() { + g_manager_list.erase(g_manager_list.iterator_to(*this)); + } + + void ManagerImpl::NotifyOne() { + this->system_event.Signal(); + } + + Result ManagerImpl::NotifyAll() { + for (auto &manager : g_manager_list) { + manager.NotifyOne(); + } + return ResultSuccess(); + } + + Result ManagerImpl::GetReportList(const ams::sf::OutBuffer &out_list, ReportType type_filter) { + R_UNLESS(out_list.GetSize() == sizeof(ReportList), erpt::ResultInvalidArgument()); + + return Journal::GetReportList(reinterpret_cast(out_list.GetPointer()), type_filter); + } + + Result ManagerImpl::GetEvent(ams::sf::OutCopyHandle out) { + out.SetValue(this->system_event.GetReadableHandle()); + return ResultSuccess(); + } + + Result ManagerImpl::CleanupReports() { + Journal::CleanupReports(); + Journal::CleanupAttachments(); + return Journal::Commit(); + } + + Result ManagerImpl::DeleteReport(const ReportId &report_id) { + R_TRY(Journal::Delete(report_id)); + return Journal::Commit(); + } + + Result ManagerImpl::GetStorageUsageStatistics(ams::sf::Out out) { + StorageUsageStatistics stats = {}; + + stats.journal_uuid = Journal::GetJournalId(); + stats.used_storage_size = Journal::GetUsedStorage(); + stats.max_report_size = Journal::GetMaxReportSize(); + + for (int i = ReportType_Start; i < ReportType_End; i++) { + const auto type = static_cast(i); + stats.report_count[i] = Journal::GetStoredReportCount(type); + stats.transmitted_count[i] = Journal::GetTransmittedCount(type); + stats.untransmitted_count[i] = Journal::GetUntransmittedCount(type); + } + + out.SetValue(stats); + return ResultSuccess(); + } + + Result ManagerImpl::GetAttachmentList(const ams::sf::OutBuffer &out_list, const ReportId &report_id) { + R_UNLESS(out_list.GetSize() == sizeof(AttachmentList), erpt::ResultInvalidArgument()); + + return Journal::GetAttachmentList(reinterpret_cast(out_list.GetPointer()), report_id); + } + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_manager_impl.hpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_manager_impl.hpp new file mode 100644 index 000000000..b75d01955 --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_manager_impl.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::erpt::srv { + + class ManagerImpl final : public erpt::sf::IManager, public util::IntrusiveListBaseNode { + private: + os::SystemEvent system_event; + public: + ManagerImpl(); + ~ManagerImpl(); + private: + void NotifyOne(); + public: + static Result NotifyAll(); + public: + virtual Result GetReportList(const ams::sf::OutBuffer &out_list, ReportType type_filter) override final; + virtual Result GetEvent(ams::sf::OutCopyHandle out) override final; + virtual Result CleanupReports() override final; + virtual Result DeleteReport(const ReportId &report_id) override final; + virtual Result GetStorageUsageStatistics(ams::sf::Out out) override final; + virtual Result GetAttachmentList(const ams::sf::OutBuffer &out_buf, const ReportId &report_id) override final; + }; + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_ref_count.hpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_ref_count.hpp new file mode 100644 index 000000000..3d61e129a --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_ref_count.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::erpt::srv { + + class RefCount { + private: + static constexpr u32 MaxReferenceCount = 1000; + std::atomic ref_count; + public: + RefCount() : ref_count(0) { /* ... */ } + + void AddReference() { + const auto prev = this->ref_count.fetch_add(1); + AMS_ABORT_UNLESS(prev <= MaxReferenceCount); + } + + bool RemoveReference() { + auto prev = this->ref_count.fetch_sub(1); + AMS_ABORT_UNLESS(prev != 0); + return prev == 1; + } + }; + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_report.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_report.cpp new file mode 100644 index 000000000..8ec7b7b1e --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_report.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "erpt_srv_report_impl.hpp" +#include "erpt_srv_report.hpp" + +namespace ams::erpt::srv { + + ReportFileName Report::FileName(ReportId report_id, bool redirect_to_sd) { + ReportFileName report_name; + std::snprintf(report_name.name, sizeof(report_name.name), + "%s:/%08x-%04x-%04x-%02x%02x-%04x%08x", + (redirect_to_sd ? ReportOnSdStoragePath : ReportStoragePath), + report_id.uuid_data.time_low, + report_id.uuid_data.time_mid, + report_id.uuid_data.time_high_and_version, + report_id.uuid_data.clock_high, + report_id.uuid_data.clock_low, + static_cast((report_id.uuid_data.node >> BITSIZEOF(u32)) & 0x0000FFFF), + static_cast((report_id.uuid_data.node >> 0) & 0xFFFFFFFF)); + return report_name; + } + + Report::Report(JournalRecord *r, bool redirect_to_sd) : record(r), redirect_to_sd_card(redirect_to_sd) { + this->record->AddReference(); + } + + Report::~Report() { + this->CloseStream(); + if (this->record->RemoveReference()) { + this->DeleteStream(this->FileName().name); + delete this->record; + } + } + + ReportFileName Report::FileName() { + return FileName(this->record->info.id, this->redirect_to_sd_card); + } + + Result Report::Open(ReportOpenType type) { + switch (type) { + case ReportOpenType_Create: return this->OpenStream(this->FileName().name, StreamMode_Write, ReportStreamBufferSize); + case ReportOpenType_Read: return this->OpenStream(this->FileName().name, StreamMode_Read, ReportStreamBufferSize); + default: return erpt::ResultInvalidArgument(); + } + } + + Result Report::Read(u32 *out_read_count, u8 *dst, u32 dst_size) { + return this->ReadStream(out_read_count, dst, dst_size); + } + + Result Report::Delete() { + return this->DeleteStream(this->FileName().name); + } + + void Report::Close() { + return this->CloseStream(); + } + + Result Report::GetFlags(ReportFlagSet *out) { + *out = this->record->info.flags; + return ResultSuccess(); + } + + Result Report::SetFlags(ReportFlagSet flags) { + if (((~this->record->info.flags) & flags).IsAnySet()) { + this->record->info.flags |= flags; + return Journal::Commit(); + } + return ResultSuccess(); + } + + Result Report::GetSize(s64 *out) { + return this->GetStreamSize(out); + } + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_report.hpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_report.hpp new file mode 100644 index 000000000..115983560 --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_report.hpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "erpt_srv_allocator.hpp" +#include "erpt_srv_stream.hpp" +#include "erpt_srv_journal.hpp" + +namespace ams::erpt::srv { + + enum ReportOpenType { + ReportOpenType_Create = 0, + ReportOpenType_Read = 1, + }; + + constexpr inline u32 ReportStreamBufferSize = 1_KB; + + class Report : public Allocator, public Stream { + private: + JournalRecord *record; + bool redirect_to_sd_card; + private: + ReportFileName FileName(); + public: + static ReportFileName FileName(ReportId report_id, bool redirect_to_sd); + public: + explicit Report(JournalRecord *r, bool redirect_to_sd); + ~Report(); + + Result Open(ReportOpenType type); + Result Read(u32 *out_read_count, u8 *dst, u32 dst_size); + Result Delete(); + void Close(); + + Result GetFlags(ReportFlagSet *out); + Result SetFlags(ReportFlagSet flags); + Result GetSize(s64 *out); + + template + Result Write(T val) { + return this->WriteStream(reinterpret_cast(std::addressof(val)), sizeof(val)); + } + + template + Result Write(const T *buf, u32 buffer_size) { + return this->WriteStream(reinterpret_cast(buf), buffer_size); + } + }; + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_report_impl.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_report_impl.cpp new file mode 100644 index 000000000..9f8cdbe72 --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_report_impl.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "erpt_srv_report_impl.hpp" +#include "erpt_srv_report.hpp" + +namespace ams::erpt::srv { + + ReportImpl::ReportImpl() : report(nullptr) { + /* ... */ + } + + ReportImpl::~ReportImpl() { + R_ABORT_UNLESS(this->Close()); + } + + Result ReportImpl::Open(const ReportId &report_id) { + R_UNLESS(this->report == nullptr, erpt::ResultAlreadyInitialized()); + + JournalRecord *record = Journal::Retrieve(report_id); + R_UNLESS(record != nullptr, erpt::ResultNotFound()); + + this->report = new Report(record, false); + R_UNLESS(this->report != nullptr, erpt::ResultOutOfMemory()); + auto report_guard = SCOPE_GUARD { delete this->report; this->report = nullptr; }; + + R_TRY(this->report->Open(ReportOpenType_Read)); + report_guard.Cancel(); + + return ResultSuccess(); + } + + Result ReportImpl::Read(ams::sf::Out out_count, const ams::sf::OutBuffer &out_buffer) { + R_UNLESS(this->report != nullptr, erpt::ResultNotInitialized()); + + return this->report->Read(out_count.GetPointer(), static_cast(out_buffer.GetPointer()), static_cast(out_buffer.GetSize())); + } + + Result ReportImpl::SetFlags(ReportFlagSet flags) { + R_UNLESS(this->report != nullptr, erpt::ResultNotInitialized()); + + return this->report->SetFlags(flags); + } + + Result ReportImpl::GetFlags(ams::sf::Out out) { + R_UNLESS(this->report != nullptr, erpt::ResultNotInitialized()); + + return this->report->GetFlags(out.GetPointer()); + } + + Result ReportImpl::Close() { + if (this->report != nullptr) { + this->report->Close(); + delete this->report; + this->report = nullptr; + } + return ResultSuccess(); + } + + Result ReportImpl::GetSize(ams::sf::Out out) { + R_UNLESS(this->report != nullptr, erpt::ResultNotInitialized()); + + return this->report->GetSize(out.GetPointer()); + } + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_report_impl.hpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_report_impl.hpp new file mode 100644 index 000000000..8b60e536a --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_report_impl.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::erpt::srv { + + class Report; + + class ReportImpl final : public erpt::sf::IReport { + private: + Report *report; + public: + ReportImpl(); + ~ReportImpl(); + public: + virtual Result Open(const ReportId &report_id) override final; + virtual Result Read(ams::sf::Out out_count, const ams::sf::OutBuffer &out_buffer) override final; + virtual Result SetFlags(ReportFlagSet flags) override final; + virtual Result GetFlags(ams::sf::Out out) override final; + virtual Result Close() override final; + virtual Result GetSize(ams::sf::Out out) override final; + }; + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.cpp new file mode 100644 index 000000000..6d3330979 --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.cpp @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "erpt_srv_reporter.hpp" +#include "erpt_srv_report.hpp" +#include "erpt_srv_journal.hpp" +#include "erpt_srv_context_record.hpp" +#include "erpt_srv_context.hpp" + +namespace ams::erpt::srv { + + bool Reporter::s_redirect_new_reports = true; + char Reporter::s_serial_number[24] = "Unknown"; + char Reporter::s_os_version[24] = "Unknown"; + char Reporter::s_private_os_version[96] = "Unknown"; + std::optional Reporter::s_application_launch_time; + std::optional Reporter::s_awake_time; + std::optional Reporter::s_power_on_time; + std::optional Reporter::s_initial_launch_settings_completion_time; + + Reporter::Reporter(ReportType type, const ContextEntry *ctx, const u8 *data, u32 data_size, const ReportMetaData *meta, const AttachmentId *attachments, u32 num_attachments) + : type(type), ctx(ctx), data(data), data_size(data_size), meta(meta), attachments(attachments), num_attachments(num_attachments), occurrence_tick() + { + /* ... */ + } + + Result Reporter::CreateReport() { + R_TRY(this->ValidateReportContext()); + R_TRY(this->CollectUniqueReportFields()); + R_TRY(this->SubmitReportDefaults()); + R_TRY(this->SubmitReportContexts()); + R_TRY(this->LinkAttachments()); + R_TRY(this->CreateReportFile()); + + this->SaveSyslogReportIfRequired(); + return ResultSuccess(); + } + + Result Reporter::ValidateReportContext() { + R_UNLESS(this->ctx->category == CategoryId_ErrorInfo, erpt::ResultRequiredContextMissing()); + R_UNLESS(this->ctx->field_count <= FieldsPerContext, erpt::ResultInvalidArgument()); + + bool found_error_code = false; + for (u32 i = 0; i < this->ctx->field_count; i++) { + if (this->ctx->fields[i].id == FieldId_ErrorCode) { + found_error_code = true; + break; + } + } + R_UNLESS(found_error_code, erpt::ResultRequiredFieldMissing()); + + return ResultSuccess(); + } + + Result Reporter::CollectUniqueReportFields() { + this->occurrence_tick = os::GetSystemTick(); + if (hos::GetVersion() >= hos::Version_300) { + this->steady_clock_internal_offset_seconds = time::GetStandardSteadyClockInternalOffset().GetSeconds(); + } else { + this->steady_clock_internal_offset_seconds = 0; + } + this->report_id.uuid = util::GenerateUuid(); + this->report_id.uuid.ToString(this->identifier_str, sizeof(this->identifier_str)); + if (R_FAILED(time::StandardNetworkSystemClock::GetCurrentTime(std::addressof(this->timestamp_network)))) { + this->timestamp_network = {0}; + } + R_ABORT_UNLESS(time::GetStandardSteadyClockCurrentTimePoint(std::addressof(this->steady_clock_current_timepoint))); + R_TRY(time::StandardUserSystemClock::GetCurrentTime(std::addressof(this->timestamp_user))); + return ResultSuccess(); + } + + Result Reporter::SubmitReportDefaults() { + bool found_abort_flag = false, found_syslog_flag = false; + for (u32 i = 0; i < this->ctx->field_count; i++) { + if (this->ctx->fields[i].id == FieldId_AbortFlag) { + found_abort_flag = true; + } + if (this->ctx->fields[i].id == FieldId_HasSyslogFlag) { + found_syslog_flag = true; + } + if (found_abort_flag && found_syslog_flag) { + break; + } + } + + ContextRecord *record = new ContextRecord(CategoryId_ErrorInfoDefaults); + R_UNLESS(record != nullptr, erpt::ResultOutOfMemory()); + auto record_guard = SCOPE_GUARD { delete record; }; + + if (!found_abort_flag) { + record->Add(FieldId_AbortFlag, false); + } + + if (!found_syslog_flag) { + record->Add(FieldId_HasSyslogFlag, true); + } + + R_TRY(Context::SubmitContextRecord(record)); + + record_guard.Cancel(); + return ResultSuccess(); + } + + Result Reporter::SubmitReportContexts() { + ContextRecord *record = new ContextRecord(CategoryId_ErrorInfoAuto); + R_UNLESS(record != nullptr, erpt::ResultOutOfMemory()); + auto record_guard = SCOPE_GUARD { delete record; }; + + record->Add(FieldId_OsVersion, s_os_version, strnlen(s_os_version, sizeof(s_os_version))); + record->Add(FieldId_PrivateOsVersion, s_private_os_version, strnlen(s_private_os_version, sizeof(s_private_os_version))); + record->Add(FieldId_SerialNumber, s_serial_number, strnlen(s_serial_number, sizeof(s_serial_number))); + record->Add(FieldId_ReportIdentifier, this->identifier_str, sizeof(this->identifier_str)); + record->Add(FieldId_OccurrenceTimestamp, this->timestamp_user.value); + record->Add(FieldId_OccurrenceTimestampNet, this->timestamp_network.value); + record->Add(FieldId_ReportVisibilityFlag, this->type == ReportType_Visible); + record->Add(FieldId_OccurrenceTick, this->occurrence_tick.GetInt64Value()); + record->Add(FieldId_SteadyClockInternalOffset, this->steady_clock_internal_offset_seconds); + record->Add(FieldId_SteadyClockCurrentTimePointValue, this->steady_clock_current_timepoint.value); + + if (s_initial_launch_settings_completion_time) { + s64 elapsed_seconds; + if (R_SUCCEEDED(time::GetElapsedSecondsBetween(std::addressof(elapsed_seconds), *s_initial_launch_settings_completion_time, this->steady_clock_current_timepoint))) { + record->Add(FieldId_ElapsedTimeSinceInitialLaunch, elapsed_seconds); + } + } + + if (s_power_on_time) { + record->Add(FieldId_ElapsedTimeSincePowerOn, (this->occurrence_tick - *s_power_on_time).ToTimeSpan().GetSeconds()); + } + + if (s_awake_time) { + record->Add(FieldId_ElapsedTimeSincePowerOn, (this->occurrence_tick - *s_awake_time).ToTimeSpan().GetSeconds()); + } + + if (s_application_launch_time) { + record->Add(FieldId_ApplicationAliveTime, (this->occurrence_tick - *s_application_launch_time).ToTimeSpan().GetSeconds()); + } + + R_TRY(Context::SubmitContextRecord(record)); + record_guard.Cancel(); + + R_TRY(Context::SubmitContext(this->ctx, this->data, this->data_size)); + + return ResultSuccess(); + } + + Result Reporter::LinkAttachments() { + for (u32 i = 0; i < this->num_attachments; i++) { + R_TRY(JournalForAttachments::SetOwner(this->attachments[i], this->report_id)); + } + return ResultSuccess(); + } + + Result Reporter::CreateReportFile() { + /* Make a journal record. */ + auto *record = new JournalRecord; + R_UNLESS(record != nullptr, erpt::ResultOutOfMemory()); + + record->AddReference(); + ON_SCOPE_EXIT { + if (record->RemoveReference()) { + delete record; + } + }; + + record->info.type = this->type; + record->info.id = this->report_id; + record->info.flags = erpt::srv::MakeNoReportFlags(); + record->info.timestamp_user = this->timestamp_user; + record->info.timestamp_network = this->timestamp_network; + if (this->meta != nullptr) { + record->info.meta_data = *this->meta; + } + if (this->num_attachments > 0) { + record->info.flags.Set(); + } + + auto report = std::make_unique(record, s_redirect_new_reports); + R_UNLESS(report != nullptr, erpt::ResultOutOfMemory()); + + R_TRY(Context::WriteContextsToReport(report.get())); + R_TRY(report->GetSize(std::addressof(record->info.report_size))); + + if (!s_redirect_new_reports) { + /* If we're not redirecting new reports, then we want to store the report in the journal. */ + R_TRY(Journal::Store(record)); + } else { + /* If we are redirecting new reports, we don't want to store the report in the journal. */ + /* We should take this opportunity to delete any attachments associated with the report. */ + R_ABORT_UNLESS(JournalForAttachments::DeleteAttachments(this->report_id)); + } + + R_TRY(Journal::Commit()); + + return ResultSuccess(); + } + + void Reporter::SaveSyslogReportIfRequired() { + bool needs_save_syslog = true; + for (u32 i = 0; i < this->ctx->field_count; i++) { + static_assert(FieldToTypeMap[FieldId_HasSyslogFlag] == FieldType_Bool); + if (this->ctx->fields[i].id == FieldId_HasSyslogFlag && (this->ctx->fields[i].value_bool == false)) { + needs_save_syslog = false; + break; + } + } + if (needs_save_syslog) { + /* Here nintendo sends a report to srepo:u (vtable offset 0xE8) with data this->report_id. */ + /* We will not send report ids to srepo:u. */ + } + } + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.hpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.hpp new file mode 100644 index 000000000..1a0e02db3 --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.hpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::erpt::srv { + + class Reporter { + private: + static bool s_redirect_new_reports; + static char s_serial_number[24]; + static char s_os_version[24]; + static char s_private_os_version[96]; + static std::optional s_application_launch_time; + static std::optional s_awake_time; + static std::optional s_power_on_time; + static std::optional s_initial_launch_settings_completion_time; + private: + const ReportType type; + const ContextEntry * const ctx; + const u8 * const data; + const u32 data_size; + const ReportMetaData * const meta; + const AttachmentId * const attachments; + const u32 num_attachments; + char identifier_str[0x40]; + time::PosixTime timestamp_user; + time::PosixTime timestamp_network; + os::Tick occurrence_tick; + s64 steady_clock_internal_offset_seconds; + ReportId report_id; + time::SteadyClockTimePoint steady_clock_current_timepoint; + public: + static void ClearApplicationLaunchTime() { s_application_launch_time = std::nullopt; } + static void ClearInitialLaunchSettingsCompletionTime() { s_initial_launch_settings_completion_time = std::nullopt; } + + static void SetInitialLaunchSettingsCompletionTime(const time::SteadyClockTimePoint &time) { s_initial_launch_settings_completion_time = time; } + + static void UpdateApplicationLaunchTime() { s_application_launch_time = os::GetSystemTick(); } + static void UpdateAwakeTime() { s_awake_time = os::GetSystemTick(); } + static void UpdatePowerOnTime() { s_power_on_time = os::GetSystemTick(); } + + static Result SetSerialNumberAndOsVersion(const char *sn, u32 sn_len, const char *os, u32 os_len, const char *os_priv, u32 os_priv_len) { + R_UNLESS(sn_len <= sizeof(s_serial_number), erpt::ResultInvalidArgument()); + R_UNLESS(os_len <= sizeof(s_os_version), erpt::ResultInvalidArgument()); + R_UNLESS(os_priv_len <= sizeof(s_private_os_version), erpt::ResultInvalidArgument()); + + std::memcpy(s_serial_number, sn, sn_len); + std::memcpy(s_os_version, os, os_len); + std::memcpy(s_private_os_version, os_priv, os_priv_len); + return ResultSuccess(); + } + + static void SetRedirectNewReportsToSdCard(bool en) { s_redirect_new_reports = en; } + private: + Result ValidateReportContext(); + Result CollectUniqueReportFields(); + Result SubmitReportDefaults(); + Result SubmitReportContexts(); + Result LinkAttachments(); + Result CreateReportFile(); + void SaveSyslogReportIfRequired(); + void SaveSyslogReport(); + public: + Reporter(ReportType type, const ContextEntry *ctx, const u8 *data, u32 data_size, const ReportMetaData *meta, const AttachmentId *attachments, u32 num_attachments); + + Result CreateReport(); + }; + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_service.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_service.cpp new file mode 100644 index 000000000..a1d08a28c --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_service.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "erpt_srv_service.hpp" +#include "erpt_srv_context_impl.hpp" +#include "erpt_srv_session_impl.hpp" +#include "erpt_srv_stream.hpp" + +namespace ams::erpt::srv { + + namespace { + + struct ErrorReportServerOptions { + static constexpr size_t PointerBufferSize = 0; + static constexpr size_t MaxDomains = 64; + static constexpr size_t MaxDomainObjects = 2 * ReportCountMax + 5 + 2; + }; + + constexpr inline size_t ErrorReportNumServers = 2; + constexpr inline size_t ErrorReportReportSessions = 5; + constexpr inline size_t ErrorReportContextSessions = 10; + constexpr inline size_t ErrorReportMaxSessions = ErrorReportReportSessions + ErrorReportContextSessions; + + constexpr inline s32 ErrorReportServerThreadPriority = 21; + + constexpr inline sm::ServiceName ErrorReportContextServiceName = sm::ServiceName::Encode("erpt:c"); + constexpr inline sm::ServiceName ErrorReportReportServiceName = sm::ServiceName::Encode("erpt:r"); + + alignas(os::ThreadStackAlignment) u8 g_server_thread_stack[16_KB]; + + class ErrorReportServiceManager : public ams::sf::hipc::ServerManager { + private: + os::ThreadType thread; + std::shared_ptr context_session_object; + private: + static void ThreadFunction(void *_this) { + reinterpret_cast(_this)->SetupAndLoopProcess(); + } + + void SetupAndLoopProcess(); + public: + ErrorReportServiceManager(erpt::srv::ContextImpl *c) + : context_session_object(ams::sf::ServiceObjectTraits::SharedPointerHelper::GetEmptyDeleteSharedPointer(c)) + { + /* ... */ + } + + Result Initialize() { + R_ABORT_UNLESS(this->RegisterServer(ErrorReportContextServiceName, ErrorReportContextSessions, this->context_session_object)); + R_ABORT_UNLESS(this->RegisterServer(ErrorReportReportServiceName, ErrorReportReportSessions)); + + this->ResumeProcessing(); + + R_ABORT_UNLESS(os::CreateThread(std::addressof(this->thread), ThreadFunction, this, g_server_thread_stack, sizeof(g_server_thread_stack), ErrorReportServerThreadPriority)); + + os::StartThread(std::addressof(this->thread)); + + return ResultSuccess(); + } + + void Wait() { + os::WaitThread(std::addressof(this->thread)); + } + }; + + void ErrorReportServiceManager::SetupAndLoopProcess() { + const psc::PmModuleId dependencies[] = { psc::PmModuleId_Fs }; + psc::PmModule pm_module; + psc::PmState pm_state; + psc::PmFlagSet pm_flags; + os::WaitableHolderType module_event_holder; + + R_ABORT_UNLESS(pm_module.Initialize(psc::PmModuleId_Erpt, dependencies, util::size(dependencies), os::EventClearMode_ManualClear)); + + os::InitializeWaitableHolder(std::addressof(module_event_holder), pm_module.GetEventPointer()->GetBase()); + os::SetWaitableHolderUserData(std::addressof(module_event_holder), static_cast(psc::PmModuleId_Erpt)); + this->AddUserWaitableHolder(std::addressof(module_event_holder)); + + while (true) { + /* NOTE: Nintendo checks the user holder data to determine what's signaled, we will prefer to just check the address. */ + auto *signaled_holder = this->WaitSignaled(); + if (signaled_holder != std::addressof(module_event_holder)) { + R_ABORT_UNLESS(this->Process(signaled_holder)); + } else { + pm_module.GetEventPointer()->Clear(); + if (R_SUCCEEDED(pm_module.GetRequest(std::addressof(pm_state), std::addressof(pm_flags)))) { + switch (pm_state) { + case psc::PmState_Awake: + case psc::PmState_ReadyAwaken: + Stream::EnableFsAccess(true); + break; + case psc::PmState_ReadySleep: + case psc::PmState_ReadyShutdown: + Stream::EnableFsAccess(false); + break; + default: + break; + } + R_ABORT_UNLESS(pm_module.Acknowledge(pm_state, ResultSuccess())); + } else { + AMS_ASSERT(false); + } + this->AddUserWaitableHolder(signaled_holder); + } + } + } + + erpt::srv::ContextImpl g_context_object; + ErrorReportServiceManager g_erpt_server_manager(std::addressof(g_context_object)); + + } + + Result InitializeService() { + return g_erpt_server_manager.Initialize(); + } + + void WaitService() { + return g_erpt_server_manager.Wait(); + } + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_service.hpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_service.hpp new file mode 100644 index 000000000..01ae0df12 --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_service.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::erpt::srv { + + Result InitializeService(); + void WaitService(); + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_session_impl.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_session_impl.cpp new file mode 100644 index 000000000..3a0eda8b0 --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_session_impl.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "erpt_srv_session_impl.hpp" +#include "erpt_srv_report_impl.hpp" +#include "erpt_srv_manager_impl.hpp" +#include "erpt_srv_attachment_impl.hpp" + +namespace ams::erpt::srv { + + Result SessionImpl::OpenReport(ams::sf::Out> out) { + /* Create an interface. */ + auto intf = std::shared_ptr(new (std::nothrow) ReportImpl); + R_UNLESS(intf != nullptr, erpt::ResultOutOfMemory()); + + /* Return it. */ + out.SetValue(std::move(intf)); + return ResultSuccess(); + } + + Result SessionImpl::OpenManager(ams::sf::Out> out) { + /* Create an interface. */ + auto intf = std::shared_ptr(new (std::nothrow) ManagerImpl); + R_UNLESS(intf != nullptr, erpt::ResultOutOfMemory()); + + /* Return it. */ + out.SetValue(std::move(intf)); + return ResultSuccess(); + } + + Result SessionImpl::OpenAttachment(ams::sf::Out> out) { + /* Create an interface. */ + auto intf = std::shared_ptr(new (std::nothrow) AttachmentImpl); + R_UNLESS(intf != nullptr, erpt::ResultOutOfMemory()); + + /* Return it. */ + out.SetValue(std::move(intf)); + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_session_impl.hpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_session_impl.hpp new file mode 100644 index 000000000..4dcc165b0 --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_session_impl.hpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::erpt::srv { + + class SessionImpl final : public erpt::sf::ISession { + public: + virtual Result OpenReport(ams::sf::Out> out) override final; + virtual Result OpenManager(ams::sf::Out> out) override final; + virtual Result OpenAttachment(ams::sf::Out> out) override final; + }; + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_stream.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_stream.cpp new file mode 100644 index 000000000..e67f0b623 --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_stream.cpp @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "erpt_srv_allocator.hpp" +#include "erpt_srv_stream.hpp" + +namespace ams::erpt::srv { + + bool Stream::s_can_access_fs = true; + + void Stream::EnableFsAccess(bool en) { + s_can_access_fs = en; + } + + Result Stream::DeleteStream(const char *path) { + R_UNLESS(s_can_access_fs, erpt::ResultInvalidPowerState()); + return fs::DeleteFile(path); + } + + Result Stream::CommitStream() { + R_UNLESS(s_can_access_fs, erpt::ResultInvalidPowerState()); + fs::CommitSaveData(ReportStoragePath); + return ResultSuccess(); + } + + Result Stream::GetStreamSize(s64 *out, const char *path) { + R_UNLESS(s_can_access_fs, erpt::ResultInvalidPowerState()); + + fs::FileHandle file; + R_TRY(fs::OpenFile(std::addressof(file), path, fs::OpenMode_Read)); + ON_SCOPE_EXIT { fs::CloseFile(file); }; + + return fs::GetFileSize(out, file); + } + + Stream::Stream() : buffer_size(0), file_position(0), buffer_count(0), buffer(nullptr), stream_mode(StreamMode_Invalid), initialized(false) { + /* ... */ + } + + Stream::~Stream() { + this->CloseStream(); + } + + Result Stream::OpenStream(const char *path, StreamMode mode, u32 buffer_size) { + R_UNLESS(s_can_access_fs, erpt::ResultInvalidPowerState()); + R_UNLESS(!this->initialized, erpt::ResultAlreadyInitialized()); + + if (mode == StreamMode_Write) { + while (true) { + R_TRY_CATCH(fs::OpenFile(std::addressof(this->file_handle), path, fs::OpenMode_Write | fs::OpenMode_AllowAppend)) { + R_CATCH(fs::ResultPathNotFound) { + R_TRY(fs::CreateFile(path, 0)); + continue; + } + } R_END_TRY_CATCH; + break; + } + fs::SetFileSize(this->file_handle, 0); + } else { + R_UNLESS(mode == StreamMode_Read, erpt::ResultInvalidArgument()); + } + auto file_guard = SCOPE_GUARD { if (mode == StreamMode_Write) { fs::CloseFile(this->file_handle); } }; + + std::strncpy(this->file_name, path, sizeof(this->file_name)); + + this->buffer = reinterpret_cast(Allocate(buffer_size)); + R_UNLESS(this->buffer != nullptr, erpt::ResultOutOfMemory()); + + this->buffer_size = buffer_size; + this->buffer_count = 0; + this->buffer_position = 0; + this->file_position = 0; + this->stream_mode = mode; + this->initialized = true; + + file_guard.Cancel(); + return ResultSuccess(); + } + + Result Stream::ReadStream(u32 *out, u8 *dst, u32 dst_size) { + R_UNLESS(s_can_access_fs, erpt::ResultInvalidPowerState()); + R_UNLESS(this->initialized, erpt::ResultNotInitialized()); + R_UNLESS(this->stream_mode == StreamMode_Read, erpt::ResultNotInitialized()); + R_UNLESS(out != nullptr, erpt::ResultInvalidArgument()); + R_UNLESS(dst != nullptr, erpt::ResultInvalidArgument()); + + fs::FileHandle tmp_file; + size_t fs_read_size; + u32 read_count = 0; + bool opened = false; + + ON_SCOPE_EXIT { + *out = read_count; + + if (opened) { + fs::CloseFile(tmp_file); + } + }; + + while (dst_size > 0) { + if (u32 cur = std::min(this->buffer_count - this->buffer_position, dst_size); cur > 0) { + std::memcpy(dst, this->buffer + this->buffer_position, cur); + this->buffer_position += cur; + dst += cur; + dst_size -= cur; + read_count += cur; + } else { + if (!opened) { + R_TRY(fs::OpenFile(std::addressof(tmp_file), this->file_name, fs::OpenMode_Read)); + opened = true; + } + + R_TRY(fs::ReadFile(std::addressof(fs_read_size), tmp_file, this->file_position, this->buffer, this->buffer_size)); + + this->buffer_position = 0; + this->file_position += static_cast(fs_read_size); + this->buffer_count = static_cast(fs_read_size); + + if (this->buffer_count == 0) { + break; + } + } + } + + return ResultSuccess(); + } + + Result Stream::WriteStream(const u8 *src, u32 src_size) { + R_UNLESS(s_can_access_fs, erpt::ResultInvalidPowerState()); + R_UNLESS(this->initialized, erpt::ResultNotInitialized()); + R_UNLESS(this->stream_mode == StreamMode_Write, erpt::ResultNotInitialized()); + R_UNLESS(src != nullptr || src_size == 0, erpt::ResultInvalidArgument()); + + while (src_size > 0) { + if (u32 cur = std::min(this->buffer_size - this->buffer_count, src_size); cur > 0) { + std::memcpy(this->buffer + this->buffer_count, src, cur); + this->buffer_count += cur; + src += cur; + src_size -= cur; + } + + if (this->buffer_count == this->buffer_size) { + R_TRY(this->Flush()); + } + } + + return ResultSuccess(); + } + + void Stream::CloseStream() { + if (this->initialized) { + if (s_can_access_fs && this->stream_mode == StreamMode_Write) { + this->Flush(); + fs::FlushFile(this->file_handle); + fs::CloseFile(this->file_handle); + } + Deallocate(this->buffer); + this->initialized = false; + } + } + + Result Stream::GetStreamSize(s64 *out) { + return GetStreamSize(out, this->file_name); + } + + Result Stream::Flush() { + R_SUCCEED_IF(this->buffer_count == 0); + R_TRY(fs::WriteFile(this->file_handle, this->file_position, this->buffer, this->buffer_count, fs::WriteOption::None)); + + this->file_position += this->buffer_count; + this->buffer_count = 0; + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_stream.hpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_stream.hpp new file mode 100644 index 000000000..b6671f692 --- /dev/null +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_stream.hpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::erpt::srv { + + enum StreamMode { + StreamMode_Write = 0, + StreamMode_Read = 1, + StreamMode_Invalid = 2, + }; + + class Stream { + private: + static bool s_can_access_fs; + private: + u32 buffer_size; + u32 file_position; + u32 buffer_position; + u32 buffer_count; + u8 *buffer; + StreamMode stream_mode; + bool initialized; + fs::FileHandle file_handle; + char file_name[ReportFileNameLength]; + public: + Stream(); + ~Stream(); + + Result OpenStream(const char *path, StreamMode mode, u32 buffer_size); + Result ReadStream(u32 *out, u8 *dst, u32 dst_size); + Result WriteStream(const u8 *src, u32 src_size); + void CloseStream(); + + Result GetStreamSize(s64 *out); + private: + Result Flush(); + public: + static void EnableFsAccess(bool en); + static Result DeleteStream(const char *path); + static Result CommitStream(); + static Result GetStreamSize(s64 *out, const char *path); + }; + +} diff --git a/libraries/libstratosphere/source/fs/fs_save_data_management.cpp b/libraries/libstratosphere/source/fs/fs_save_data_management.cpp index 6185029fe..639930b50 100644 --- a/libraries/libstratosphere/source/fs/fs_save_data_management.cpp +++ b/libraries/libstratosphere/source/fs/fs_save_data_management.cpp @@ -116,4 +116,24 @@ namespace ams::fs { return impl::WriteSaveDataFileSystemExtraData(space_id, id, extra_data); } + Result GetSaveDataAvailableSize(s64 *out, SaveDataId id) { + SaveDataExtraData extra_data; + R_TRY(impl::ReadSaveDataFileSystemExtraData(std::addressof(extra_data), id)); + + *out = extra_data.available_size; + return ResultSuccess(); + } + + Result GetSaveDataJournalSize(s64 *out, SaveDataId id) { + SaveDataExtraData extra_data; + R_TRY(impl::ReadSaveDataFileSystemExtraData(std::addressof(extra_data), id)); + + *out = extra_data.journal_size; + return ResultSuccess(); + } + + Result ExtendSaveData(SaveDataSpaceId space_id, SaveDataId id, s64 available_size, s64 journal_size) { + return ::fsExtendSaveDataFileSystem(static_cast<::FsSaveDataSpaceId>(space_id), static_cast(id), available_size, journal_size); + } + } diff --git a/libraries/libstratosphere/source/fs/fs_sd_card.cpp b/libraries/libstratosphere/source/fs/fs_sd_card.cpp index 27472d76f..66c22c2c5 100644 --- a/libraries/libstratosphere/source/fs/fs_sd_card.cpp +++ b/libraries/libstratosphere/source/fs/fs_sd_card.cpp @@ -20,6 +20,8 @@ namespace ams::fs { namespace { + constexpr inline const char AtmosphereErrorReportDirectory[] = "/atmosphere/erpt_reports"; + /* NOTE: Nintendo does not attach a generator to a mounted SD card filesystem. */ /* However, it is desirable for homebrew to be able to access SD via common path. */ class SdCardCommonMountNameGenerator : public fsa::ICommonMountNameGenerator, public impl::Newable { @@ -63,4 +65,27 @@ namespace ams::fs { return fsa::Register(name, std::move(fsa), std::move(generator)); } + Result MountSdCardErrorReportDirectoryForAtmosphere(const char *name) { + /* Validate the mount name. */ + R_TRY(impl::CheckMountName(name)); + + /* Open the SD card. This uses libnx bindings. */ + FsFileSystem fs; + R_TRY(fsOpenSdCardFileSystem(std::addressof(fs))); + + /* Allocate a new filesystem wrapper. */ + std::unique_ptr fsa = std::make_unique(fs); + R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInSdCardA()); + + /* Ensure that the error report directory exists. */ + R_TRY(fssystem::EnsureDirectoryRecursively(fsa.get(), AtmosphereErrorReportDirectory)); + + /* Create a subdirectory filesystem. */ + auto subdir_fs = std::make_unique(std::move(fsa), AtmosphereErrorReportDirectory); + R_UNLESS(subdir_fs != nullptr, fs::ResultAllocationFailureInSdCardA()); + + /* Register. */ + return fsa::Register(name, std::move(subdir_fs)); + } + } diff --git a/libraries/libstratosphere/source/nim/nim_network_install_manager_api.cpp b/libraries/libstratosphere/source/nim/nim_network_install_manager_api.cpp index 156f924c8..85640a06e 100644 --- a/libraries/libstratosphere/source/nim/nim_network_install_manager_api.cpp +++ b/libraries/libstratosphere/source/nim/nim_network_install_manager_api.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * Copyright (c) 2019-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, diff --git a/libraries/libstratosphere/source/psc/psc_pm_module.os.horizon.cpp b/libraries/libstratosphere/source/psc/psc_pm_module.os.horizon.cpp new file mode 100644 index 000000000..4ef5887a8 --- /dev/null +++ b/libraries/libstratosphere/source/psc/psc_pm_module.os.horizon.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "psc_remote_pm_module.hpp" + +namespace ams::psc { + + PmModule::PmModule() : intf(nullptr), initialized(false), reserved(0) { /* ... */ } + + PmModule::~PmModule() { + if (this->initialized) { + this->intf = nullptr; + os::DestroySystemEvent(this->system_event.GetBase()); + } + } + + Result PmModule::Initialize(const PmModuleId mid, const PmModuleId *dependencies, u32 dependency_count, os::EventClearMode clear_mode) { + R_UNLESS(!this->initialized, psc::ResultAlreadyInitialized()); + + static_assert(sizeof(*dependencies) == sizeof(u16)); + ::PscPmModule module; + R_TRY(::pscmGetPmModule(std::addressof(module), static_cast<::PscPmModuleId>(mid), reinterpret_cast(dependencies), dependency_count, clear_mode == os::EventClearMode_AutoClear)); + + this->intf = std::make_shared(module); + this->system_event.AttachReadableHandle(module.event.revent, false, clear_mode); + this->initialized = true; + return ResultSuccess(); + } + + Result PmModule::Finalize() { + R_UNLESS(this->initialized, psc::ResultNotInitialized()); + + R_TRY(this->intf->Finalize()); + this->intf = nullptr; + os::DestroySystemEvent(this->system_event.GetBase()); + this->initialized = false; + return ResultSuccess(); + } + + Result PmModule::GetRequest(PmState *out_state, PmFlagSet *out_flags) { + R_UNLESS(this->initialized, psc::ResultNotInitialized()); + + return this->intf->GetRequest(out_state, out_flags); + } + + Result PmModule::Acknowledge(PmState state, Result res) { + R_ABORT_UNLESS(res); + R_UNLESS(this->initialized, psc::ResultNotInitialized()); + + if (hos::GetVersion() >= hos::Version_600) { + return this->intf->AcknowledgeEx(state); + } else { + return this->intf->Acknowledge(); + } + } + + os::SystemEvent *PmModule::GetEventPointer() { + return std::addressof(this->system_event); + } + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/psc/psc_remote_pm_module.hpp b/libraries/libstratosphere/source/psc/psc_remote_pm_module.hpp new file mode 100644 index 000000000..ff6451a62 --- /dev/null +++ b/libraries/libstratosphere/source/psc/psc_remote_pm_module.hpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::psc { + + class RemotePmModule final : public psc::sf::IPmModule { + NON_COPYABLE(RemotePmModule); + NON_MOVEABLE(RemotePmModule); + private: + ::PscPmModule module; + public: + constexpr RemotePmModule(const ::PscPmModule &m) : module(m) { /* ... */ } + virtual ~RemotePmModule() override { + ::pscPmModuleClose(std::addressof(this->module)); + } + + virtual Result Initialize(ams::sf::OutCopyHandle out, psc::PmModuleId module_id, const ams::sf::InBuffer &child_list) override final { + /* NOTE: This functionality is already implemented by the libnx command we use to instantiate the PscPmModule. */ + AMS_ABORT(); + } + + virtual Result GetRequest(ams::sf::Out out_state, ams::sf::Out out_flags) override final { + static_assert(sizeof(PmState) == sizeof(::PscPmState)); + static_assert(sizeof(PmFlagSet) == sizeof(u32)); + return ::pscPmModuleGetRequest(std::addressof(this->module), reinterpret_cast<::PscPmState *>(out_state.GetPointer()), reinterpret_cast(out_flags.GetPointer())); + } + + virtual Result Acknowledge() override final { + /* NOTE: libnx does not separate acknowledge/acknowledgeEx. */ + return ::pscPmModuleAcknowledge(std::addressof(this->module), static_cast<::PscPmState>(0)); + } + + virtual Result Finalize() override final { + return ::pscPmModuleFinalize(std::addressof(this->module)); + } + + virtual Result AcknowledgeEx(PmState state) override final { + static_assert(sizeof(state) == sizeof(::PscPmState)); + return ::pscPmModuleAcknowledge(std::addressof(this->module), static_cast<::PscPmState>(state)); + } + }; + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/settings/impl/settings_firmware_version_impl.cpp b/libraries/libstratosphere/source/settings/impl/settings_firmware_version_impl.cpp new file mode 100644 index 000000000..9688b3a86 --- /dev/null +++ b/libraries/libstratosphere/source/settings/impl/settings_firmware_version_impl.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "settings_firmware_version_impl.hpp" + +namespace ams::settings::impl { + + Result GetFirmwareVersion(settings::system::FirmwareVersion *out) { + static_assert(sizeof(*out) == sizeof(::SetSysFirmwareVersion)); + return ::setsysGetFirmwareVersion(reinterpret_cast<::SetSysFirmwareVersion *>(out)); + } + +} diff --git a/libraries/libstratosphere/source/settings/impl/settings_firmware_version_impl.hpp b/libraries/libstratosphere/source/settings/impl/settings_firmware_version_impl.hpp new file mode 100644 index 000000000..f8f3507ba --- /dev/null +++ b/libraries/libstratosphere/source/settings/impl/settings_firmware_version_impl.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::settings::impl { + + Result GetFirmwareVersion(settings::system::FirmwareVersion *out); + +} diff --git a/libraries/libstratosphere/source/settings/impl/settings_product_model_impl.cpp b/libraries/libstratosphere/source/settings/impl/settings_product_model_impl.cpp new file mode 100644 index 000000000..7f94510c9 --- /dev/null +++ b/libraries/libstratosphere/source/settings/impl/settings_product_model_impl.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "settings_product_model_impl.hpp" + +namespace ams::settings::impl { + + Result GetProductModel(s32 *out) { + return ::setsysGetProductModel(out); + } + +} diff --git a/libraries/libstratosphere/source/settings/impl/settings_product_model_impl.hpp b/libraries/libstratosphere/source/settings/impl/settings_product_model_impl.hpp new file mode 100644 index 000000000..f5af580ae --- /dev/null +++ b/libraries/libstratosphere/source/settings/impl/settings_product_model_impl.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::settings::impl { + + Result GetProductModel(s32 *out); + +} diff --git a/libraries/libstratosphere/source/settings/impl/settings_region_impl.cpp b/libraries/libstratosphere/source/settings/impl/settings_region_impl.cpp new file mode 100644 index 000000000..5597dbba2 --- /dev/null +++ b/libraries/libstratosphere/source/settings/impl/settings_region_impl.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "settings_region_impl.hpp" + +namespace ams::settings::impl { + + Result GetRegionCode(s32 *out) { + static_assert(sizeof(*out) == sizeof(::SetRegion)); + return ::setGetRegionCode(reinterpret_cast<::SetRegion *>(out)); + } + +} diff --git a/libraries/libstratosphere/source/settings/impl/settings_region_impl.hpp b/libraries/libstratosphere/source/settings/impl/settings_region_impl.hpp new file mode 100644 index 000000000..98a9404d3 --- /dev/null +++ b/libraries/libstratosphere/source/settings/impl/settings_region_impl.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::settings::impl { + + Result GetRegionCode(s32 *out); + +} diff --git a/libraries/libstratosphere/source/settings/impl/settings_serial_number_impl.cpp b/libraries/libstratosphere/source/settings/impl/settings_serial_number_impl.cpp new file mode 100644 index 000000000..56a073c02 --- /dev/null +++ b/libraries/libstratosphere/source/settings/impl/settings_serial_number_impl.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "settings_serial_number_impl.hpp" + +namespace ams::settings::impl { + + Result GetSerialNumber(settings::system::SerialNumber *out) { + static_assert(sizeof(*out) == sizeof(::SetSysSerialNumber)); + return ::setsysGetSerialNumber(reinterpret_cast<::SetSysSerialNumber *>(out)); + } + +} diff --git a/libraries/libstratosphere/source/settings/impl/settings_serial_number_impl.hpp b/libraries/libstratosphere/source/settings/impl/settings_serial_number_impl.hpp new file mode 100644 index 000000000..87df5e90a --- /dev/null +++ b/libraries/libstratosphere/source/settings/impl/settings_serial_number_impl.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::settings::impl { + + Result GetSerialNumber(settings::system::SerialNumber *out); + +} diff --git a/libraries/libstratosphere/source/settings/settings_firmware_version.cpp b/libraries/libstratosphere/source/settings/settings_firmware_version.cpp new file mode 100644 index 000000000..faa069c5c --- /dev/null +++ b/libraries/libstratosphere/source/settings/settings_firmware_version.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "impl/settings_firmware_version_impl.hpp" + +namespace ams::settings::system { + + void GetFirmwareVersion(FirmwareVersion *out) { + R_ABORT_UNLESS(settings::impl::GetFirmwareVersion(out)); + } + +} diff --git a/libraries/libstratosphere/source/settings/settings_product_model.cpp b/libraries/libstratosphere/source/settings/settings_product_model.cpp new file mode 100644 index 000000000..dc1ee2c80 --- /dev/null +++ b/libraries/libstratosphere/source/settings/settings_product_model.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "impl/settings_product_model_impl.hpp" + +namespace ams::settings::system { + + ProductModel GetProductModel() { + s32 model = 0; + R_ABORT_UNLESS(settings::impl::GetProductModel(std::addressof(model))); + return static_cast(model); + } + +} diff --git a/libraries/libstratosphere/source/settings/settings_region.cpp b/libraries/libstratosphere/source/settings/settings_region.cpp new file mode 100644 index 000000000..824a968b6 --- /dev/null +++ b/libraries/libstratosphere/source/settings/settings_region.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "impl/settings_region_impl.hpp" + +namespace ams::settings::system { + + void GetRegionCode(RegionCode *out) { + AMS_ABORT_UNLESS(out != nullptr); + s32 value = 0; + R_ABORT_UNLESS(settings::impl::GetRegionCode(std::addressof(value))); + *out = static_cast(value); + } + +} diff --git a/libraries/libstratosphere/source/settings/settings_serial_number.cpp b/libraries/libstratosphere/source/settings/settings_serial_number.cpp new file mode 100644 index 000000000..05e17c545 --- /dev/null +++ b/libraries/libstratosphere/source/settings/settings_serial_number.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "impl/settings_serial_number_impl.hpp" + +namespace ams::settings::system { + + void GetSerialNumber(SerialNumber *out) { + R_ABORT_UNLESS(settings::impl::GetSerialNumber(out)); + } + +} diff --git a/libraries/libstratosphere/source/time/impl/util/time_impl_util_api.cpp b/libraries/libstratosphere/source/time/impl/util/time_impl_util_api.cpp new file mode 100644 index 000000000..6a6435d4a --- /dev/null +++ b/libraries/libstratosphere/source/time/impl/util/time_impl_util_api.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::time::impl::util { + + Result GetSpanBetween(s64 *out, const SteadyClockTimePoint &from, const SteadyClockTimePoint &to) { + AMS_ASSERT(out != nullptr); + + R_UNLESS(out != nullptr, time::ResultInvalidPointer()); + R_UNLESS(from.source_id == to.source_id, time::ResultNotComparable()); + + const bool no_overflow = (from.value >= 0 ? (to.value >= std::numeric_limits::min() + from.value) + : (to.value <= std::numeric_limits::max() + from.value)); + R_UNLESS(no_overflow, time::ResultOverflowed()); + + *out = to.value - from.value; + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/time/time_api.cpp b/libraries/libstratosphere/source/time/time_api.cpp new file mode 100644 index 000000000..80f7741b9 --- /dev/null +++ b/libraries/libstratosphere/source/time/time_api.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2019-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +extern "C" { + + extern TimeServiceType __nx_time_service_type; + +} + +namespace ams::time { + + namespace { + + enum InitializeMode { + InitializeMode_None, + InitializeMode_Normal, + InitializeMode_Menu, + InitializeMode_System, + InitializeMode_Repair, + InitializeMode_SystemUser, + }; + + u32 g_initialize_count = 0; + InitializeMode g_initialize_mode = InitializeMode_None; + + /* TODO: os::SdkMutex */ + os::Mutex g_initialize_mutex(false); + + Result InitializeImpl(InitializeMode mode) { + std::scoped_lock lk(g_initialize_mutex); + + if (g_initialize_count > 0) { + AMS_ABORT_UNLESS(mode == g_initialize_mode); + g_initialize_count++; + return ResultSuccess(); + } + + switch (mode) { + case InitializeMode_Normal: __nx_time_service_type = ::TimeServiceType_User; break; + case InitializeMode_Menu: __nx_time_service_type = ::TimeServiceType_Menu; break; + case InitializeMode_System: __nx_time_service_type = ::TimeServiceType_System; break; + case InitializeMode_Repair: __nx_time_service_type = ::TimeServiceType_Repair; break; + case InitializeMode_SystemUser: __nx_time_service_type = ::TimeServiceType_SystemUser; break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + R_TRY(::timeInitialize()); + + g_initialize_count++; + g_initialize_mode = mode; + return ResultSuccess(); + } + + } + + Result Initialize() { + return InitializeImpl(InitializeMode_Normal); + } + + Result InitializeForSystem() { + return InitializeImpl(InitializeMode_System); + } + + Result InitializeForSystemUser() { + return InitializeImpl(InitializeMode_System); + } + + Result Finalize() { + std::scoped_lock lk(g_initialize_mutex); + + if (g_initialize_count > 0) { + if ((--g_initialize_count) == 0) { + ::timeExit(); + g_initialize_mode = InitializeMode_None; + } + } + + return ResultSuccess(); + } + + bool IsInitialized() { + std::scoped_lock lk(g_initialize_mutex); + + return g_initialize_count > 0; + } + + Result GetElapsedSecondsBetween(s64 *out, const SteadyClockTimePoint &from, const SteadyClockTimePoint &to) { + return impl::util::GetSpanBetween(out, from, to); + } + +} diff --git a/libraries/libstratosphere/source/time/time_standard_network_system_clock.cpp b/libraries/libstratosphere/source/time/time_standard_network_system_clock.cpp new file mode 100644 index 000000000..8df139b46 --- /dev/null +++ b/libraries/libstratosphere/source/time/time_standard_network_system_clock.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::time { + + Result StandardNetworkSystemClock::GetCurrentTime(PosixTime *out) { + static_assert(sizeof(*out) == sizeof(u64)); + return ::timeGetCurrentTime(::TimeType_NetworkSystemClock, reinterpret_cast(out)); + } + + StandardNetworkSystemClock::time_point StandardNetworkSystemClock::now() { + PosixTime posix_time = {}; + if (R_FAILED(GetCurrentTime(std::addressof(posix_time)))) { + posix_time.value = 0; + } + + return time_point(duration(posix_time.value)); + } + + std::time_t StandardNetworkSystemClock::to_time_t(const StandardNetworkSystemClock::time_point &t) { + return static_cast(std::chrono::duration_cast(t.time_since_epoch()).count()); + } + + StandardNetworkSystemClock::time_point StandardNetworkSystemClock::from_time_t(std::time_t t) { + return time_point(duration(t)); + } + + /* TODO: Result StandardNetworkSystemClock::GetSystemClockContext(SystemClockContext *out); */ + +} diff --git a/libraries/libstratosphere/source/time/time_standard_steady_clock.cpp b/libraries/libstratosphere/source/time/time_standard_steady_clock.cpp new file mode 100644 index 000000000..16364317a --- /dev/null +++ b/libraries/libstratosphere/source/time/time_standard_steady_clock.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::time { + + Result GetStandardSteadyClockCurrentTimePoint(SteadyClockTimePoint *out) { + static_assert(sizeof(*out) == sizeof(::TimeSteadyClockTimePoint)); + return ::timeGetStandardSteadyClockTimePoint(reinterpret_cast<::TimeSteadyClockTimePoint *>(out)); + } + + TimeSpan GetStandardSteadyClockInternalOffset() { + static_assert(sizeof(TimeSpanType) == sizeof(s64)); + TimeSpanType offset; + R_ABORT_UNLESS(::timeGetStandardSteadyClockInternalOffset(reinterpret_cast(std::addressof(offset)))); + return offset; + } + + Result StandardSteadyClock::GetCurrentTimePoint(SteadyClockTimePoint *out) { + return GetStandardSteadyClockCurrentTimePoint(out); + } + + StandardSteadyClock::time_point StandardSteadyClock::now() { + SteadyClockTimePoint steady_clock_time_point = {0, util::InvalidUuid}; + if (R_FAILED(StandardSteadyClock::GetCurrentTimePoint(std::addressof(steady_clock_time_point)))) { + steady_clock_time_point.value = 0; + steady_clock_time_point.source_id = util::InvalidUuid; + } + + return StandardSteadyClock::time_point(StandardSteadyClock::duration(steady_clock_time_point.value)); + } + +} diff --git a/libraries/libstratosphere/source/time/time_standard_user_system_clock.cpp b/libraries/libstratosphere/source/time/time_standard_user_system_clock.cpp new file mode 100644 index 000000000..b56d0528e --- /dev/null +++ b/libraries/libstratosphere/source/time/time_standard_user_system_clock.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::time { + + Result StandardUserSystemClock::GetCurrentTime(PosixTime *out) { + static_assert(sizeof(*out) == sizeof(u64)); + return ::timeGetCurrentTime(::TimeType_UserSystemClock, reinterpret_cast(out)); + } + + StandardUserSystemClock::time_point StandardUserSystemClock::now() { + PosixTime posix_time = {}; + if (R_FAILED(GetCurrentTime(std::addressof(posix_time)))) { + posix_time.value = 0; + } + + return time_point(duration(posix_time.value)); + } + + std::time_t StandardUserSystemClock::to_time_t(const StandardUserSystemClock::time_point &t) { + return static_cast(std::chrono::duration_cast(t.time_since_epoch()).count()); + } + + StandardUserSystemClock::time_point StandardUserSystemClock::from_time_t(std::time_t t) { + return time_point(duration(t)); + } + + /* TODO: Result StandardUserSystemClock::GetSystemClockContext(SystemClockContext *out); */ + +} diff --git a/libraries/libvapours/include/vapours/crypto.hpp b/libraries/libvapours/include/vapours/crypto.hpp index 6ef2ed14d..6fd9c0487 100644 --- a/libraries/libvapours/include/vapours/crypto.hpp +++ b/libraries/libvapours/include/vapours/crypto.hpp @@ -28,3 +28,5 @@ #include #include #include +#include +#include diff --git a/libraries/libvapours/include/vapours/crypto/crypto_csrng.hpp b/libraries/libvapours/include/vapours/crypto/crypto_csrng.hpp new file mode 100644 index 000000000..76f9134ee --- /dev/null +++ b/libraries/libvapours/include/vapours/crypto/crypto_csrng.hpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include + +namespace ams::crypto { + + void GenerateCryptographicallyRandomBytes(void *dst, size_t dst_size); + +} diff --git a/libraries/libvapours/include/vapours/crypto/crypto_rsa_oaep_decryptor.hpp b/libraries/libvapours/include/vapours/crypto/crypto_rsa_oaep_decryptor.hpp index 98280c44e..73bbeacf3 100644 --- a/libraries/libvapours/include/vapours/crypto/crypto_rsa_oaep_decryptor.hpp +++ b/libraries/libvapours/include/vapours/crypto/crypto_rsa_oaep_decryptor.hpp @@ -45,7 +45,7 @@ namespace ams::crypto { u8 label_digest[HashSize]; State state; public: - RsaOaepDecryptor() : set_label_digest(false), state(State::None) { /* ... */ } + RsaOaepDecryptor() : set_label_digest(false), state(State::None) { std::memset(this->label_digest, 0, sizeof(this->label_digest)); } ~RsaOaepDecryptor() { ClearMemory(this->label_digest, sizeof(this->label_digest)); @@ -78,21 +78,22 @@ namespace ams::crypto { size_t Decrypt(void *dst, size_t dst_size, const void *src, size_t src_size) { AMS_ASSERT(this->state == State::Initialized); - ON_SCOPE_EXIT { this->state = State::Done; }; impl::RsaOaepImpl impl; u8 message[BlockSize]; ON_SCOPE_EXIT { ClearMemory(message, sizeof(message)); }; if (!this->calculator.ExpMod(message, src, src_size)) { + std::memset(dst, 0, dst_size); return false; } if (!this->set_label_digest) { this->hash.GetHash(this->label_digest, sizeof(this->label_digest)); - this->set_label_digest = true; } + ON_SCOPE_EXIT { this->state = State::Done; }; + return impl.Decode(dst, dst_size, this->label_digest, sizeof(this->label_digest), message, sizeof(message)); } diff --git a/libraries/libvapours/include/vapours/crypto/crypto_rsa_oaep_encryptor.hpp b/libraries/libvapours/include/vapours/crypto/crypto_rsa_oaep_encryptor.hpp new file mode 100644 index 000000000..bd29e4e2a --- /dev/null +++ b/libraries/libvapours/include/vapours/crypto/crypto_rsa_oaep_encryptor.hpp @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include +#include +#include + +namespace ams::crypto { + + template /* requires HashFunction */ + class RsaOaepEncryptor { + NON_COPYABLE(RsaOaepEncryptor); + NON_MOVEABLE(RsaOaepEncryptor); + public: + static constexpr size_t HashSize = Hash::HashSize; + static constexpr size_t BlockSize = ModulusSize; + static constexpr size_t MaximumExponentSize = 3; + static constexpr size_t RequiredWorkBufferSize = RsaCalculator::RequiredWorkBufferSize; + private: + enum class State { + None, + Initialized, + Done, + }; + private: + RsaCalculator calculator; + Hash hash; + bool set_label_digest; + u8 label_digest[HashSize]; + State state; + public: + RsaOaepEncryptor() : set_label_digest(false), state(State::None) { std::memset(this->label_digest, 0, sizeof(this->label_digest)); } + + ~RsaOaepEncryptor() { + ClearMemory(this->label_digest, sizeof(this->label_digest)); + } + + bool Initialize(const void *mod, size_t mod_size, const void *exp, size_t exp_size) { + this->hash.Initialize(); + this->set_label_digest = false; + if (this->calculator.Initialize(mod, mod_size, exp, exp_size)) { + this->state = State::Initialized; + return true; + } else { + return false; + } + } + + void UpdateLabel(const void *data, size_t size) { + AMS_ASSERT(this->state == State::Initialized); + + this->hash.Update(data, size); + } + + void SetLabelDigest(const void *digest, size_t digest_size) { + AMS_ASSERT(this->state == State::Initialized); + AMS_ABORT_UNLESS(digest_size == sizeof(this->label_digest)); + + std::memcpy(this->label_digest, digest, digest_size); + this->set_label_digest = true; + } + + bool Encrypt(void *dst, size_t dst_size, const void *src, size_t src_size, const void *salt, size_t salt_size) { + AMS_ASSERT(this->state == State::Initialized); + + impl::RsaOaepImpl impl; + if (!this->set_label_digest) { + this->hash.GetHash(this->label_digest, sizeof(this->label_digest)); + } + + impl.Encode(dst, dst_size, this->label_digest, sizeof(this->label_digest), src, src_size, salt, salt_size); + + if (!this->calculator.ExpMod(dst, dst, dst_size)) { + std::memset(dst, 0, dst_size); + return false; + } + + this->state = State::Done; + return true; + } + + bool Encrypt(void *dst, size_t dst_size, const void *src, size_t src_size, const void *salt, size_t salt_size, void *work, size_t work_size) { + AMS_ASSERT(this->state == State::Initialized); + + impl::RsaOaepImpl impl; + if (!this->set_label_digest) { + this->hash.GetHash(this->label_digest, sizeof(this->label_digest)); + } + + impl.Encode(dst, dst_size, this->label_digest, sizeof(this->label_digest), src, src_size, salt, salt_size); + + if (!this->calculator.ExpMod(dst, dst, dst_size, work, work_size)) { + std::memset(dst, 0, dst_size); + return false; + } + + this->state = State::Done; + return true; + } + + static bool Encrypt(void *dst, size_t dst_size, const void *mod, size_t mod_size, const void *exp, size_t exp_size, const void *msg, size_t msg_size, const void *seed, size_t seed_size, const void *lab, size_t lab_size) { + RsaOaepEncryptor oaep; + if (!oaep.Initialize(mod, mod_size, exp, exp_size)) { + return false; + } + oaep.UpdateLabel(lab, lab_size); + return oaep.Encrypt(dst, dst_size, msg, msg_size, seed, seed_size); + } + + static bool Encrypt(void *dst, size_t dst_size, const void *mod, size_t mod_size, const void *exp, size_t exp_size, const void *msg, size_t msg_size, const void *seed, size_t seed_size, const void *lab, size_t lab_size, void *work, size_t work_size) { + RsaOaepEncryptor oaep; + if (!oaep.Initialize(mod, mod_size, exp, exp_size)) { + return false; + } + oaep.UpdateLabel(lab, lab_size); + return oaep.Encrypt(dst, dst_size, msg, msg_size, seed, seed_size, work, work_size); + } + + }; + +} diff --git a/libraries/libvapours/include/vapours/crypto/crypto_rsa_oaep_sha256_encryptor.hpp b/libraries/libvapours/include/vapours/crypto/crypto_rsa_oaep_sha256_encryptor.hpp new file mode 100644 index 000000000..d06c540a8 --- /dev/null +++ b/libraries/libvapours/include/vapours/crypto/crypto_rsa_oaep_sha256_encryptor.hpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include +#include +#include +#include +#include + +namespace ams::crypto { + + namespace impl { + + template + using RsaNOaepSha256Encryptor = ::ams::crypto::RsaOaepEncryptor; + + } + + using Rsa2048OaepSha256Encryptor = ::ams::crypto::impl::RsaNOaepSha256Encryptor<2048>; + using Rsa4096OaepSha256Encryptor = ::ams::crypto::impl::RsaNOaepSha256Encryptor<4096>; + + inline size_t EncryptRsa2048OaepSha256(void *dst, size_t dst_size, const void *mod, size_t mod_size, const void *exp, size_t exp_size, const void *msg, size_t msg_size, const void *salt, size_t salt_size, const void *lab, size_t lab_size) { + return Rsa2048OaepSha256Encryptor::Encrypt(dst, dst_size, mod, mod_size, exp, exp_size, msg, msg_size, salt, salt_size, lab, lab_size); + } + + inline size_t EncryptRsa2048OaepSha256(void *dst, size_t dst_size, const void *mod, size_t mod_size, const void *exp, size_t exp_size, const void *msg, size_t msg_size, const void *salt, size_t salt_size, const void *lab, size_t lab_size, void *work_buf, size_t work_buf_size) { + return Rsa2048OaepSha256Encryptor::Encrypt(dst, dst_size, mod, mod_size, exp, exp_size, msg, msg_size, salt, salt_size, lab, lab_size, work_buf, work_buf_size); + } + + inline size_t EncryptRsa4096OaepSha256(void *dst, size_t dst_size, const void *mod, size_t mod_size, const void *exp, size_t exp_size, const void *msg, size_t msg_size, const void *salt, size_t salt_size, const void *lab, size_t lab_size) { + return Rsa4096OaepSha256Encryptor::Encrypt(dst, dst_size, mod, mod_size, exp, exp_size, msg, msg_size, salt, salt_size, lab, lab_size); + } + + inline size_t EncryptRsa4096OaepSha256(void *dst, size_t dst_size, const void *mod, size_t mod_size, const void *exp, size_t exp_size, const void *msg, size_t msg_size, const void *salt, size_t salt_size, const void *lab, size_t lab_size, void *work_buf, size_t work_buf_size) { + return Rsa4096OaepSha256Encryptor::Encrypt(dst, dst_size, mod, mod_size, exp, exp_size, msg, msg_size, salt, salt_size, lab, lab_size, work_buf, work_buf_size); + } + +} diff --git a/libraries/libvapours/include/vapours/crypto/impl/crypto_rsa_oaep_impl.hpp b/libraries/libvapours/include/vapours/crypto/impl/crypto_rsa_oaep_impl.hpp index bfe6afce5..d925f16a8 100644 --- a/libraries/libvapours/include/vapours/crypto/impl/crypto_rsa_oaep_impl.hpp +++ b/libraries/libvapours/include/vapours/crypto/impl/crypto_rsa_oaep_impl.hpp @@ -71,6 +71,39 @@ namespace ams::crypto::impl { public: RsaOaepImpl() { /* ... */ } + void Encode(void *dst, size_t dst_size, Hash *hash, const void *src, size_t src_size, const void *salt, size_t salt_size) { + u8 label_digest[HashSize]; + ON_SCOPE_EXIT { ClearMemory(label_digest, HashSize); }; + + hash->GetHash(label_digest, HashSize); + return this->Encode(dst, dst_size, label_digest, sizeof(label_digest), src, src_size, salt, salt_size); + } + + void Encode(void *dst, size_t dst_size, const void *label_digest, size_t label_digest_size, const void *src, size_t src_size, const void *salt, size_t salt_size) { + /* Check our preconditions. */ + AMS_ASSERT(dst_size >= 2 * HashSize + 2 + src_size); + AMS_ASSERT(salt_size > 0); + AMS_ASSERT(salt_size == HashSize); + AMS_ASSERT(label_digest_size == HashSize); + + u8 *buf = static_cast(dst); + buf[0] = HeadMagic; + + u8 *seed = buf + 1; + std::memcpy(seed, salt, HashSize); + + u8 *db = seed + HashSize; + std::memcpy(db, label_digest, HashSize); + std::memset(db + HashSize, 0, dst_size - 2 * HashSize - 2 - src_size); + + u8 *msg = buf + dst_size - src_size - 1; + *(msg++) = 0x01; + std::memcpy(msg, src, src_size); + + ApplyMGF1(db, dst_size - (1 + HashSize), seed, HashSize); + ApplyMGF1(seed, HashSize, db, dst_size - (1 + HashSize)); + } + size_t Decode(void *dst, size_t dst_size, const void *label_digest, size_t label_digest_size, u8 *buf, size_t buf_size) { /* Check our preconditions. */ AMS_ABORT_UNLESS(dst_size > 0); diff --git a/libraries/libvapours/include/vapours/results.hpp b/libraries/libvapours/include/vapours/results.hpp index 3cdae3a8d..77c762b67 100644 --- a/libraries/libvapours/include/vapours/results.hpp +++ b/libraries/libvapours/include/vapours/results.hpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -38,12 +39,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include diff --git a/libraries/libvapours/include/vapours/results/erpt_results.hpp b/libraries/libvapours/include/vapours/results/erpt_results.hpp new file mode 100644 index 000000000..4993474dc --- /dev/null +++ b/libraries/libvapours/include/vapours/results/erpt_results.hpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +namespace ams::erpt { + + R_DEFINE_NAMESPACE_RESULT_MODULE(147); + + R_DEFINE_ERROR_RESULT(NotInitialized, 1); + R_DEFINE_ERROR_RESULT(AlreadyInitialized, 2); + R_DEFINE_ERROR_RESULT(OutOfArraySpace, 3); + R_DEFINE_ERROR_RESULT(OutOfFieldSpace, 4); + R_DEFINE_ERROR_RESULT(OutOfMemory, 5); + R_DEFINE_ERROR_RESULT(InvalidArgument, 7); + R_DEFINE_ERROR_RESULT(NotFound, 8); + R_DEFINE_ERROR_RESULT(FieldCategoryMismatch, 9); + R_DEFINE_ERROR_RESULT(FieldTypeMismatch, 10); + R_DEFINE_ERROR_RESULT(AlreadyExists, 11); + R_DEFINE_ERROR_RESULT(CorruptJournal, 12); + R_DEFINE_ERROR_RESULT(CategoryNotFound, 13); + R_DEFINE_ERROR_RESULT(RequiredContextMissing, 14); + R_DEFINE_ERROR_RESULT(RequiredFieldMissing, 15); + R_DEFINE_ERROR_RESULT(FormatterError, 16); + R_DEFINE_ERROR_RESULT(InvalidPowerState, 17); + R_DEFINE_ERROR_RESULT(ArrayFieldTooLarge, 18); + R_DEFINE_ERROR_RESULT(AlreadyOwned, 19); + +} diff --git a/libraries/libvapours/include/vapours/results/psc_results.hpp b/libraries/libvapours/include/vapours/results/psc_results.hpp new file mode 100644 index 000000000..883e1b32f --- /dev/null +++ b/libraries/libvapours/include/vapours/results/psc_results.hpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +namespace ams::psc { + + R_DEFINE_NAMESPACE_RESULT_MODULE(138); + + R_DEFINE_ERROR_RESULT(AlreadyInitialized, 2); + R_DEFINE_ERROR_RESULT(NotInitialized, 3); + +} diff --git a/libraries/libvapours/include/vapours/results/time_results.hpp b/libraries/libvapours/include/vapours/results/time_results.hpp new file mode 100644 index 000000000..f82e05566 --- /dev/null +++ b/libraries/libvapours/include/vapours/results/time_results.hpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +namespace ams::time { + + R_DEFINE_NAMESPACE_RESULT_MODULE(116); + + R_DEFINE_ERROR_RESULT(NotInitialized, 0); + + R_DEFINE_ERROR_RESULT(NotComparable, 200); + R_DEFINE_ERROR_RESULT(Overflowed, 201); + + R_DEFINE_ABSTRACT_ERROR_RANGE(InvalidArgument, 900, 919); + R_DEFINE_ERROR_RESULT(InvalidPointer, 901); + +} diff --git a/libraries/libvapours/include/vapours/util.hpp b/libraries/libvapours/include/vapours/util.hpp index f6d8a68c2..3048f515a 100644 --- a/libraries/libvapours/include/vapours/util.hpp +++ b/libraries/libvapours/include/vapours/util.hpp @@ -34,3 +34,4 @@ #include #include #include +#include diff --git a/libraries/libvapours/include/vapours/util/util_bitflagset.hpp b/libraries/libvapours/include/vapours/util/util_bitflagset.hpp index 2c421afd6..4ccf07ccd 100644 --- a/libraries/libvapours/include/vapours/util/util_bitflagset.hpp +++ b/libraries/libvapours/include/vapours/util/util_bitflagset.hpp @@ -178,9 +178,9 @@ namespace ams::util { constexpr BitFlagSet operator^(const BitFlagSet &rhs) const { BitFlagSet v = *this; v ^= rhs; return v; } constexpr BitFlagSet operator|(const BitFlagSet &rhs) const { BitFlagSet v = *this; v |= rhs; return v; } - constexpr BitFlagSet &operator&=(const BitFlagSet &rhs) const { ams::util::impl::AndImpl(this->_storage, rhs._storage); return *this; } - constexpr BitFlagSet &operator^=(const BitFlagSet &rhs) const { ams::util::impl::XorImpl(this->_storage, rhs._storage); return *this; } - constexpr BitFlagSet &operator|=(const BitFlagSet &rhs) const { ams::util::impl::OrImpl(this->_storage, rhs._storage); return *this; } + constexpr BitFlagSet &operator&=(const BitFlagSet &rhs) { ams::util::impl::AndImpl(this->_storage, rhs._storage); return *this; } + constexpr BitFlagSet &operator^=(const BitFlagSet &rhs) { ams::util::impl::XorImpl(this->_storage, rhs._storage); return *this; } + constexpr BitFlagSet &operator|=(const BitFlagSet &rhs) { ams::util::impl::OrImpl(this->_storage, rhs._storage); return *this; } }; template diff --git a/libraries/libvapours/include/vapours/util/util_string_util.hpp b/libraries/libvapours/include/vapours/util/util_string_util.hpp new file mode 100644 index 000000000..a15367690 --- /dev/null +++ b/libraries/libvapours/include/vapours/util/util_string_util.hpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +namespace ams::util { + + template + constexpr int Strlcpy(T *dst, const T *src, int count) { + AMS_ASSERT(dst != nullptr); + AMS_ASSERT(src != nullptr); + + const T *cur = src; + if (count > 0) { + while ((--count) && *cur) { + *(dst++) = *(cur++); + } + *dst = 0; + } + + while (*cur) { + cur++; + } + + return static_cast(cur - src); + } + +} diff --git a/libraries/libvapours/include/vapours/util/util_uuid.hpp b/libraries/libvapours/include/vapours/util/util_uuid.hpp index 28bca41ce..2a75a9797 100644 --- a/libraries/libvapours/include/vapours/util/util_uuid.hpp +++ b/libraries/libvapours/include/vapours/util/util_uuid.hpp @@ -21,20 +21,60 @@ namespace ams::util { struct Uuid { - static constexpr size_t Size = 0x10; + static constexpr size_t Size = 0x10; + static constexpr size_t StringSize = sizeof("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"); u8 data[Size]; - bool operator==(const Uuid &rhs) const { - return std::memcmp(this->data, rhs.data, Size) == 0; + friend bool operator==(const Uuid &lhs, const Uuid &rhs) { + return std::memcmp(lhs.data, rhs.data, Size) == 0; } - bool operator!=(const Uuid &rhs) const { - return !(*this == rhs); + friend bool operator!=(const Uuid &lhs, const Uuid &rhs) { + return !(lhs == rhs); } - u8 operator[](size_t i) const { - return this->data[i]; + const char *ToString(char *dst, size_t dst_size) const { + std::snprintf(dst, dst_size, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + this->data[ 0], this->data[ 1], this->data[ 2], this->data[ 3], this->data[ 4], this->data[ 5], this->data[ 6], this->data[ 7], + this->data[ 8], this->data[ 9], this->data[10], this->data[11], this->data[12], this->data[13], this->data[14], this->data[15]); + + return dst; + } + + void FromString(const char *str) { + char buf[2 + 1] = {}; + char *end; + s32 i = 0; + + for (/* ... */; i < 4; ++i, str += 2) { + std::memcpy(buf, str, 2); + this->data[i] = static_cast(std::strtoul(buf, std::addressof(end), 16)); + } + ++str; + + for (/* ... */; i < 6; ++i, str += 2) { + std::memcpy(buf, str, 2); + this->data[i] = static_cast(std::strtoul(buf, std::addressof(end), 16)); + } + ++str; + + for (/* ... */; i < 8; ++i, str += 2) { + std::memcpy(buf, str, 2); + this->data[i] = static_cast(std::strtoul(buf, std::addressof(end), 16)); + } + ++str; + + for (/* ... */; i < 10; ++i, str += 2) { + std::memcpy(buf, str, 2); + this->data[i] = static_cast(std::strtoul(buf, std::addressof(end), 16)); + } + ++str; + + for (/* ... */; i < 16; ++i, str += 2) { + std::memcpy(buf, str, 2); + this->data[i] = static_cast(std::strtoul(buf, std::addressof(end), 16)); + } } }; diff --git a/stratosphere/erpt/Makefile b/stratosphere/erpt/Makefile new file mode 100644 index 000000000..7320b9f99 --- /dev/null +++ b/stratosphere/erpt/Makefile @@ -0,0 +1,128 @@ +#--------------------------------------------------------------------------------- +# pull in common stratosphere sysmodule configuration +#--------------------------------------------------------------------------------- +include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/stratosphere.mk + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + + +CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ + $(notdir $(wildcard $(dir)/*.c)))) +CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) +CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) +CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) + +CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ + $(notdir $(wildcard $(dir)/*.cpp)))) +CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) +CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) +CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) + +SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ + $(notdir $(wildcard $(dir)/*.s)))) +SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) +SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) +SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) + +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC) + +ifeq ($(strip $(CONFIG_JSON)),) + jsons := $(wildcard *.json) + ifneq (,$(findstring $(TARGET).json,$(jsons))) + export APP_JSON := $(TOPDIR)/$(TARGET).json + else + ifneq (,$(findstring config.json,$(jsons))) + export APP_JSON := $(TOPDIR)/config.json + endif + endif +else + export APP_JSON := $(TOPDIR)/$(CONFIG_JSON) +endif + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).nsp $(TARGET).npdm $(TARGET).nso $(TARGET).elf + + +#--------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +all : $(OUTPUT).nsp + +ifeq ($(strip $(APP_JSON)),) +$(OUTPUT).nsp : $(OUTPUT).nso +else +$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm +endif + +$(OUTPUT).nso : $(OUTPUT).elf + +$(OUTPUT).elf : $(OFILES) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/stratosphere/erpt/erpt.json b/stratosphere/erpt/erpt.json new file mode 100644 index 000000000..a0d914b7d --- /dev/null +++ b/stratosphere/erpt/erpt.json @@ -0,0 +1,88 @@ +{ + "name": "erpt", + "title_id": "0x010000000000002b", + "title_id_range_min": "0x010000000000002b", + "title_id_range_max": "0x010000000000002b", + "main_thread_stack_size": "0x00001000", + "main_thread_priority": 49, + "default_cpu_id": 3, + "process_category": 0, + "is_retail": true, + "pool_partition": 2, + "is_64_bit": true, + "address_space_type": 1, + "filesystem_access": { + "permissions": "0xFFFFFFFFFFFFFFFF" + }, + "service_access": ["lm", "fsp-srv", "time:u", "set", "set:sys", "srepo:u", "psc:m", "csrng"], + "service_host": ["erpt:r", "erpt:c"], + "kernel_capabilities": [{ + "type": "kernel_flags", + "value": { + "highest_thread_priority": 63, + "lowest_thread_priority": 24, + "lowest_cpu_id": 3, + "highest_cpu_id": 3 + } + }, { + "type": "syscalls", + "value": { + "svcSetHeapSize": "0x01", + "svcSetMemoryPermission": "0x02", + "svcSetMemoryAttribute": "0x03", + "svcMapMemory": "0x04", + "svcUnmapMemory": "0x05", + "svcQueryMemory": "0x06", + "svcExitProcess": "0x07", + "svcCreateThread": "0x08", + "svcStartThread": "0x09", + "svcExitThread": "0x0a", + "svcSleepThread": "0x0b", + "svcGetThreadPriority": "0x0c", + "svcSetThreadPriority": "0x0d", + "svcGetThreadCoreMask": "0x0e", + "svcSetThreadCoreMask": "0x0f", + "svcGetCurrentProcessorNumber": "0x10", + "svcSignalEvent": "0x11", + "svcClearEvent": "0x12", + "svcMapSharedMemory": "0x13", + "svcUnmapSharedMemory": "0x14", + "svcCreateTransferMemory": "0x15", + "svcCloseHandle": "0x16", + "svcResetSignal": "0x17", + "svcWaitSynchronization": "0x18", + "svcCancelSynchronization": "0x19", + "svcArbitrateLock": "0x1a", + "svcArbitrateUnlock": "0x1b", + "svcWaitProcessWideKeyAtomic": "0x1c", + "svcSignalProcessWideKey": "0x1d", + "svcGetSystemTick": "0x1e", + "svcConnectToNamedPort": "0x1f", + "svcSendSyncRequestLight": "0x20", + "svcSendSyncRequest": "0x21", + "svcSendSyncRequestWithUserBuffer": "0x22", + "svcSendAsyncRequestWithUserBuffer": "0x23", + "svcGetProcessId": "0x24", + "svcGetThreadId": "0x25", + "svcBreak": "0x26", + "svcOutputDebugString": "0x27", + "svcReturnFromException": "0x28", + "svcGetInfo": "0x29", + "svcWaitForAddress": "0x34", + "svcSignalToAddress": "0x35", + "svcCreateSession": "0x40", + "svcAcceptSession": "0x41", + "svcReplyAndReceiveLight": "0x42", + "svcReplyAndReceive": "0x43", + "svcReplyAndReceiveWithUserBuffer": "0x44", + "svcCreateEvent": "0x45", + "svcCallSecureMonitor": "0x7f" + } + }, { + "type": "min_kernel_version", + "value": "0x0030" + }, { + "type": "handle_table_size", + "value": 256 + }] +} \ No newline at end of file diff --git a/stratosphere/erpt/source/erpt_main.cpp b/stratosphere/erpt/source/erpt_main.cpp new file mode 100644 index 000000000..7b2d3451b --- /dev/null +++ b/stratosphere/erpt/source/erpt_main.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +extern "C" { + extern u32 __start__; + + u32 __nx_applet_type = AppletType_None; + u32 __nx_fs_num_sessions = 1; + + #define INNER_HEAP_SIZE 0x4000 + size_t nx_inner_heap_size = INNER_HEAP_SIZE; + char nx_inner_heap[INNER_HEAP_SIZE]; + + void __libnx_initheap(void); + void __appInit(void); + void __appExit(void); + + /* Exception handling. */ + alignas(16) u8 __nx_exception_stack[ams::os::MemoryPageSize]; + u64 __nx_exception_stack_size = sizeof(__nx_exception_stack); + void __libnx_exception_handler(ThreadExceptionDump *ctx); +} + +namespace ams { + + ncm::ProgramId CurrentProgramId = ncm::SystemProgramId::Erpt; + + namespace result { + + bool CallFatalOnResultAssertion = false; + + } + +} + +using namespace ams; + +void __libnx_exception_handler(ThreadExceptionDump *ctx) { + ams::CrashHandler(ctx); +} + +void __libnx_initheap(void) { + void* addr = nx_inner_heap; + size_t size = nx_inner_heap_size; + + /* Newlib */ + extern char* fake_heap_start; + extern char* fake_heap_end; + + fake_heap_start = (char*)addr; + fake_heap_end = (char*)addr + size; +} + +void __appInit(void) { + hos::SetVersionForLibnx(); + + sm::DoWithSession([&]() { + R_ABORT_UNLESS(setInitialize()); + R_ABORT_UNLESS(setsysInitialize()); + R_ABORT_UNLESS(pscmInitialize()); + R_ABORT_UNLESS(time::Initialize()); + R_ABORT_UNLESS(fsInitialize()); + }); + + ams::CheckApiVersion(); +} + +void __appExit(void) { + fsExit(); + time::Finalize(); + pscmExit(); + setsysExit(); + setExit(); +} + +namespace ams::erpt { + + namespace { + + constexpr size_t MemoryHeapSize = 196_KB; + alignas(os::MemoryPageSize) u8 g_memory_heap[MemoryHeapSize]; + + } + + int MakeProductModelString(char *dst, size_t dst_size, settings::system::ProductModel model) { + switch (model) { + case settings::system::ProductModel_Invalid: return util::Strlcpy(dst, "Invalid", static_cast(dst_size)); + case settings::system::ProductModel_Nx: return util::Strlcpy(dst, "NX", static_cast(dst_size)); + default: return std::snprintf(dst, dst_size, "%d", static_cast(model)); + } + } + + const char *GetRegionString(settings::system::RegionCode code) { + switch (code) { + case settings::system::RegionCode_Japan: return "Japan"; + case settings::system::RegionCode_Usa: return "Usa"; + case settings::system::RegionCode_Europe: return "Europe"; + case settings::system::RegionCode_Australia: return "Australia"; + case settings::system::RegionCode_HongKongTaiwanKorea: return "HongKongTaiwanKorea"; + case settings::system::RegionCode_China: return "China"; + default: return "RegionUnknown"; + } + } + +} + +int main(int argc, char **argv) +{ + /* Set the memory heap for erpt::srv namespace. */ + R_ABORT_UNLESS(erpt::srv::Initialize(erpt::g_memory_heap, erpt::MemoryHeapSize)); + + /* Atmosphere always wants to redirect new reports to the SD card, to prevent them from being logged. */ + erpt::srv::SetRedirectNewReportsToSdCard(true); + + /* Configure the OS version. */ + { + settings::system::FirmwareVersion firmware_version = {}; + settings::system::SerialNumber serial_number = {}; + settings::system::GetFirmwareVersion(std::addressof(firmware_version)); + settings::system::GetSerialNumber(std::addressof(serial_number)); + + char os_private[0x60]; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-truncation" + const auto os_priv_len = std::snprintf(os_private, sizeof(os_private), "%s (%.8s)", firmware_version.display_name, firmware_version.revision); +#pragma GCC diagnostic pop + AMS_ASSERT(static_cast(os_priv_len) < sizeof(os_private)); + + R_ABORT_UNLESS(erpt::srv::SetSerialNumberAndOsVersion(serial_number.str, + strnlen(serial_number.str, sizeof(serial_number.str) - 1) + 1, + firmware_version.display_version, + strnlen(firmware_version.display_version, sizeof(firmware_version.display_version) - 1) + 1, + os_private, + strnlen(os_private, sizeof(os_private) - 1) + 1)); + } + + /* Configure the product model. */ + { + char product_model[0x10]; + const auto pm_len = erpt::MakeProductModelString(product_model, sizeof(product_model), settings::system::GetProductModel()); + AMS_ASSERT(static_cast(pm_len) < sizeof(product_model)); + R_ABORT_UNLESS(erpt::srv::SetProductModel(product_model, static_cast(std::strlen(product_model)))); + } + + /* Configure the region. */ + { + settings::system::RegionCode code; + settings::system::GetRegionCode(std::addressof(code)); + const char *region_str = erpt::GetRegionString(code); + R_ABORT_UNLESS(erpt::srv::SetRegionSetting(region_str, static_cast(std::strlen(region_str)))); + } + + /* Start the erpt server. */ + R_ABORT_UNLESS(erpt::srv::InitializeAndStartService()); + + /* Wait forever. */ + erpt::srv::Wait(); + + /* Cleanup */ + return 0; +} + diff --git a/stratosphere/fatal/source/fatal_config.cpp b/stratosphere/fatal/source/fatal_config.cpp index 47f936f94..bbebe52a6 100644 --- a/stratosphere/fatal/source/fatal_config.cpp +++ b/stratosphere/fatal/source/fatal_config.cpp @@ -60,8 +60,8 @@ namespace ams::fatal::srv { std::memset(this, 0, sizeof(*this)); /* Get information from set. */ - setsysGetSerialNumber(this->serial_number); - setsysGetFirmwareVersion(&this->firmware_version); + settings::system::GetSerialNumber(std::addressof(this->serial_number)); + settings::system::GetFirmwareVersion(std::addressof(this->firmware_version)); setsysGetQuestFlag(&this->quest_flag); this->UpdateLanguageCode(); diff --git a/stratosphere/fatal/source/fatal_config.hpp b/stratosphere/fatal/source/fatal_config.hpp index 91f190fbb..b34e79525 100644 --- a/stratosphere/fatal/source/fatal_config.hpp +++ b/stratosphere/fatal/source/fatal_config.hpp @@ -20,8 +20,8 @@ namespace ams::fatal::srv { class FatalConfig { private: - char serial_number[0x18]; - SetSysFirmwareVersion firmware_version; + settings::system::SerialNumber serial_number; + settings::system::FirmwareVersion firmware_version; u64 language_code; u64 quest_reboot_interval_second; bool transition_to_fatal; @@ -35,11 +35,11 @@ namespace ams::fatal::srv { public: FatalConfig(); - const char *GetSerialNumber() const { + const settings::system::SerialNumber &GetSerialNumber() const { return this->serial_number; } - const SetSysFirmwareVersion &GetFirmwareVersion() const { + const settings::system::FirmwareVersion &GetFirmwareVersion() const { return this->firmware_version; }