mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2026-06-05 19:56:57 +00:00
erpt: Implement 22.0.0 commands and changes
Co-authored-by: nvnprogram <97150065+nvnprogram@users.noreply.github.com>
This commit is contained in:
parent
f028802fb8
commit
1847db06f8
@ -243,4 +243,33 @@ namespace ams::erpt {
|
|||||||
Map16 = 0xDE,
|
Map16 = 0xDE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr inline u32 ErrorCodeSizeMax = 15;
|
||||||
|
constexpr inline u32 ProgramIdSizeMax = 17;
|
||||||
|
|
||||||
|
struct RecentReportEntry {
|
||||||
|
char error_code[ErrorCodeSizeMax];
|
||||||
|
char program_id[ProgramIdSizeMax];
|
||||||
|
u8 is_visible;
|
||||||
|
u8 is_system_abort;
|
||||||
|
u8 is_application_abort;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(RecentReportEntry) == 35);
|
||||||
|
|
||||||
|
struct RecentReportSummary : public sf::LargeData, public sf::PrefersAutoSelectTransferMode {
|
||||||
|
u32 entry_count;
|
||||||
|
RecentReportEntry entries[50];
|
||||||
|
char firmware_display_version[0x18];
|
||||||
|
char private_os_version[96];
|
||||||
|
char product_model[16];
|
||||||
|
char region_code[34];
|
||||||
|
};
|
||||||
|
static_assert(sizeof(RecentReportSummary) == 0x784);
|
||||||
|
|
||||||
|
struct SystemInfo {
|
||||||
|
char os_version[0x18];
|
||||||
|
char private_os_version[96];
|
||||||
|
char product_model[16];
|
||||||
|
const char *region;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,6 +25,7 @@
|
|||||||
AMS_SF_METHOD_INFO(C, H, 4, Result, GetStorageUsageStatistics, (ams::sf::Out<erpt::StorageUsageStatistics> out), (out), hos::Version_5_0_0) \
|
AMS_SF_METHOD_INFO(C, H, 4, Result, GetStorageUsageStatistics, (ams::sf::Out<erpt::StorageUsageStatistics> out), (out), hos::Version_5_0_0) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 5, Result, GetAttachmentListDeprecated, (const ams::sf::OutBuffer &out_buf, const erpt::ReportId &report_id), (out_buf, report_id), hos::Version_8_0_0, hos::Version_19_0_1) \
|
AMS_SF_METHOD_INFO(C, H, 5, Result, GetAttachmentListDeprecated, (const ams::sf::OutBuffer &out_buf, const erpt::ReportId &report_id), (out_buf, report_id), hos::Version_8_0_0, hos::Version_19_0_1) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 6, Result, GetAttachmentList, (ams::sf::Out<u32> out_count, const ams::sf::OutBuffer &out_buf, const erpt::ReportId &report_id), (out_count, out_buf, report_id), hos::Version_20_0_0) \
|
AMS_SF_METHOD_INFO(C, H, 6, Result, GetAttachmentList, (ams::sf::Out<u32> out_count, const ams::sf::OutBuffer &out_buf, const erpt::ReportId &report_id), (out_count, out_buf, report_id), hos::Version_20_0_0) \
|
||||||
|
AMS_SF_METHOD_INFO(C, H, 7, Result, GetRecentReportSummary, (ams::sf::Out<erpt::RecentReportSummary> out), (out), hos::Version_22_0_0) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 10, Result, GetReportSizeMax, (ams::sf::Out<u32> out), (out), hos::Version_20_0_0)
|
AMS_SF_METHOD_INFO(C, H, 10, Result, GetReportSizeMax, (ams::sf::Out<u32> out), (out), hos::Version_20_0_0)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -21,7 +21,9 @@ namespace ams::erpt::srv {
|
|||||||
Result Initialize(u8 *mem, size_t mem_size);
|
Result Initialize(u8 *mem, size_t mem_size);
|
||||||
Result InitializeAndStartService();
|
Result InitializeAndStartService();
|
||||||
|
|
||||||
Result SetSerialNumberAndOsVersion(const char *sn, u32 sn_len, const char *os, u32 os_len, const char *os_priv, u32 os_priv_len);
|
const SystemInfo &GetSystemInfo();
|
||||||
|
|
||||||
|
Result SetSerialNumber(const char *sn, u32 sn_len);
|
||||||
Result SetProductModel(const char *model, u32 model_len);
|
Result SetProductModel(const char *model, u32 model_len);
|
||||||
Result SetRegionSetting(const char *region, u32 region_len);
|
Result SetRegionSetting(const char *region, u32 region_len);
|
||||||
|
|
||||||
|
|||||||
@ -84,7 +84,7 @@ namespace ams::erpt::srv {
|
|||||||
Result JournalForAttachments::GetAttachmentList(u32 *out_count, AttachmentInfo *out_infos, size_t max_out_infos, ReportId report_id) {
|
Result JournalForAttachments::GetAttachmentList(u32 *out_count, AttachmentInfo *out_infos, size_t max_out_infos, ReportId report_id) {
|
||||||
if (hos::GetVersion() >= hos::Version_20_0_0) {
|
if (hos::GetVersion() >= hos::Version_20_0_0) {
|
||||||
/* TODO: What define gives a minimum of 10? */
|
/* TODO: What define gives a minimum of 10? */
|
||||||
R_UNLESS(max_out_infos >= 10, erpt::ResultInvalidArgument());
|
R_UNLESS(max_out_infos >= 10, erpt::ResultTooManyOutAttachments());
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 count = 0;
|
u32 count = 0;
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
#include "erpt_srv_journal.hpp"
|
#include "erpt_srv_journal.hpp"
|
||||||
#include "erpt_srv_service.hpp"
|
#include "erpt_srv_service.hpp"
|
||||||
#include "erpt_srv_forced_shutdown.hpp"
|
#include "erpt_srv_forced_shutdown.hpp"
|
||||||
|
#include "erpt_srv_recent_report.hpp"
|
||||||
|
|
||||||
namespace ams::erpt::srv {
|
namespace ams::erpt::srv {
|
||||||
|
|
||||||
@ -33,6 +34,7 @@ namespace ams::erpt::srv {
|
|||||||
constexpr u32 SystemSaveDataFlags = fs::SaveDataFlags_KeepAfterResettingSystemSaveDataWithoutUserSaveData;
|
constexpr u32 SystemSaveDataFlags = fs::SaveDataFlags_KeepAfterResettingSystemSaveDataWithoutUserSaveData;
|
||||||
constexpr s64 SystemSaveDataSize = 11_MB;
|
constexpr s64 SystemSaveDataSize = 11_MB;
|
||||||
constexpr s64 SystemSaveDataJournalSize = 2720_KB;
|
constexpr s64 SystemSaveDataJournalSize = 2720_KB;
|
||||||
|
constexpr u32 DefaultThrottleTimeWindowSeconds = 3;
|
||||||
|
|
||||||
constinit bool g_automatic_report_cleanup_enabled = true;
|
constinit bool g_automatic_report_cleanup_enabled = true;
|
||||||
|
|
||||||
@ -53,7 +55,7 @@ namespace ams::erpt::srv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result MountSystemSaveData() {
|
Result MountSystemSaveData() {
|
||||||
if (hos::GetVersion() < hos::Version_22_0_0) {
|
if (hos::GetVersion() < hos::Version_21_0_0) {
|
||||||
fs::DisableAutoSaveDataCreation();
|
fs::DisableAutoSaveDataCreation();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,6 +75,72 @@ namespace ams::erpt::srv {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
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<int>(dst_size));
|
||||||
|
case settings::system::ProductModel_Nx: return util::Strlcpy(dst, "NX", static_cast<int>(dst_size));
|
||||||
|
default: return util::SNPrintf(dst, dst_size, "%d", static_cast<int>(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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const erpt::SystemInfo &GetSystemInfo() {
|
||||||
|
static const erpt::SystemInfo s_info = [] {
|
||||||
|
erpt::SystemInfo info = {};
|
||||||
|
|
||||||
|
settings::system::FirmwareVersion firmware_version = {};
|
||||||
|
settings::system::GetFirmwareVersion(std::addressof(firmware_version));
|
||||||
|
|
||||||
|
util::Strlcpy(info.os_version, firmware_version.display_version, sizeof(info.os_version));
|
||||||
|
|
||||||
|
const auto os_priv_len = util::SNPrintf(info.private_os_version, sizeof(info.private_os_version), "%s (%.8s)", firmware_version.display_name, firmware_version.revision);
|
||||||
|
AMS_ASSERT(static_cast<size_t>(os_priv_len) < sizeof(info.private_os_version));
|
||||||
|
AMS_UNUSED(os_priv_len);
|
||||||
|
|
||||||
|
const auto pm_len = MakeProductModelString(info.product_model, sizeof(info.product_model), settings::system::GetProductModel());
|
||||||
|
AMS_ASSERT(static_cast<size_t>(pm_len) < sizeof(info.product_model));
|
||||||
|
AMS_UNUSED(pm_len);
|
||||||
|
|
||||||
|
settings::system::RegionCode region_code;
|
||||||
|
settings::system::GetRegionCode(std::addressof(region_code));
|
||||||
|
info.region = GetRegionString(region_code);
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}();
|
||||||
|
return s_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetThrottleTimeWindowSecondsImpl() {
|
||||||
|
u32 seconds = DefaultThrottleTimeWindowSeconds;
|
||||||
|
if (settings::fwdbg::GetSettingsItemValue(std::addressof(seconds), sizeof(seconds), "erpt", "throttle_time_window_seconds") != sizeof(seconds)) {
|
||||||
|
return DefaultThrottleTimeWindowSeconds;
|
||||||
|
}
|
||||||
|
return seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetReportThrottleTimeSpan() {
|
||||||
|
u32 seconds = GetThrottleTimeWindowSecondsImpl();
|
||||||
|
|
||||||
|
const TimeSpan time_span = TimeSpan::FromSeconds(static_cast<s64>(seconds));
|
||||||
|
|
||||||
|
Reporter::SetThrottleTimeSpan(time_span);
|
||||||
|
}
|
||||||
|
|
||||||
Result Initialize(u8 *mem, size_t mem_size) {
|
Result Initialize(u8 *mem, size_t mem_size) {
|
||||||
R_ABORT_UNLESS(time::Initialize());
|
R_ABORT_UNLESS(time::Initialize());
|
||||||
|
|
||||||
@ -103,6 +171,10 @@ namespace ams::erpt::srv {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hos::GetVersion() >= hos::Version_22_0_0) {
|
||||||
|
SetReportThrottleTimeSpan();
|
||||||
|
}
|
||||||
|
|
||||||
R_ABORT_UNLESS(MountSystemSaveData());
|
R_ABORT_UNLESS(MountSystemSaveData());
|
||||||
|
|
||||||
g_sf_allocator.Attach(g_heap_handle);
|
g_sf_allocator.Attach(g_heap_handle);
|
||||||
@ -112,8 +184,18 @@ namespace ams::erpt::srv {
|
|||||||
AMS_ABORT_UNLESS(ctx != nullptr);
|
AMS_ABORT_UNLESS(ctx != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (R_FAILED(Journal::Restore())) {
|
if (hos::GetVersion() >= hos::Version_21_0_0) {
|
||||||
/* TODO: Nintendo deletes system savedata when this fails. Should we?. */
|
/* >= 21.0.0, Nintendo checks the result of restore and deletes the save data if it fails. */
|
||||||
|
if (R_FAILED(Journal::Restore())) {
|
||||||
|
/* Delete and recreate the system save data. */
|
||||||
|
fs::Unmount(ReportStoragePath);
|
||||||
|
R_ABORT_UNLESS(fs::DeleteSystemSaveData(fs::SaveDataSpaceId::System, SystemSaveDataId, fs::InvalidUserId));
|
||||||
|
|
||||||
|
R_ABORT_UNLESS(MountSystemSaveData());
|
||||||
|
}
|
||||||
|
} else{
|
||||||
|
/* Pre 21.0.0, Nintendo just calls restore and ignores the result. */
|
||||||
|
Journal::Restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
Reporter::UpdatePowerOnTime();
|
Reporter::UpdatePowerOnTime();
|
||||||
@ -130,8 +212,8 @@ namespace ams::erpt::srv {
|
|||||||
R_RETURN(InitializeService());
|
R_RETURN(InitializeService());
|
||||||
}
|
}
|
||||||
|
|
||||||
Result SetSerialNumberAndOsVersion(const char *sn, u32 sn_len, const char *os, u32 os_len, const char *os_priv, u32 os_priv_len) {
|
Result SetSerialNumber(const char *sn, u32 sn_len) {
|
||||||
R_RETURN(Reporter::SetSerialNumberAndOsVersion(sn, sn_len, os, os_len, os_priv, os_priv_len));
|
R_RETURN(Reporter::SetSerialNumber(sn, sn_len));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result SetProductModel(const char *model, u32 model_len) {
|
Result SetProductModel(const char *model, u32 model_len) {
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "erpt_srv_manager_impl.hpp"
|
#include "erpt_srv_manager_impl.hpp"
|
||||||
#include "erpt_srv_journal.hpp"
|
#include "erpt_srv_journal.hpp"
|
||||||
|
#include "erpt_srv_recent_report.hpp"
|
||||||
|
|
||||||
namespace ams::erpt::srv {
|
namespace ams::erpt::srv {
|
||||||
|
|
||||||
@ -59,6 +60,7 @@ namespace ams::erpt::srv {
|
|||||||
Result ManagerImpl::CleanupReports() {
|
Result ManagerImpl::CleanupReports() {
|
||||||
Journal::CleanupReports();
|
Journal::CleanupReports();
|
||||||
Journal::CleanupAttachments();
|
Journal::CleanupAttachments();
|
||||||
|
RecentReport::Clear();
|
||||||
R_RETURN(Journal::Commit());
|
R_RETURN(Journal::Commit());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,9 +101,15 @@ namespace ams::erpt::srv {
|
|||||||
|
|
||||||
Result ManagerImpl::GetReportSizeMax(ams::sf::Out<u32> out) {
|
Result ManagerImpl::GetReportSizeMax(ams::sf::Out<u32> out) {
|
||||||
/* TODO: Where is this size defined? */
|
/* TODO: Where is this size defined? */
|
||||||
constexpr size_t ReportSizeMax = 0x3FF4F;
|
constexpr size_t ReportSizeMax = 0x35D3D;
|
||||||
*out = ReportSizeMax;
|
*out = ReportSizeMax;
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result ManagerImpl::GetRecentReportSummary(ams::sf::Out<RecentReportSummary> out) {
|
||||||
|
RecentReport::GetSummary(out.GetPointer());
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,6 +36,7 @@ namespace ams::erpt::srv {
|
|||||||
Result GetStorageUsageStatistics(ams::sf::Out<StorageUsageStatistics> out);
|
Result GetStorageUsageStatistics(ams::sf::Out<StorageUsageStatistics> out);
|
||||||
Result GetAttachmentListDeprecated(const ams::sf::OutBuffer &out_buf, const ReportId &report_id);
|
Result GetAttachmentListDeprecated(const ams::sf::OutBuffer &out_buf, const ReportId &report_id);
|
||||||
Result GetAttachmentList(ams::sf::Out<u32> out_count, const ams::sf::OutBuffer &out_buf, const ReportId &report_id);
|
Result GetAttachmentList(ams::sf::Out<u32> out_count, const ams::sf::OutBuffer &out_buf, const ReportId &report_id);
|
||||||
|
Result GetRecentReportSummary(ams::sf::Out<RecentReportSummary> out);
|
||||||
Result GetReportSizeMax(ams::sf::Out<u32> out);
|
Result GetReportSizeMax(ams::sf::Out<u32> out);
|
||||||
};
|
};
|
||||||
static_assert(erpt::sf::IsIManager<ManagerImpl>);
|
static_assert(erpt::sf::IsIManager<ManagerImpl>);
|
||||||
|
|||||||
@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "erpt_srv_recent_report.hpp"
|
||||||
|
|
||||||
|
namespace ams::erpt::srv {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr size_t MaxEntriesPerType = 25;
|
||||||
|
|
||||||
|
struct RecentReportState {
|
||||||
|
u32 report_counts[ReportType_Count];
|
||||||
|
RecentReportEntry report_entries[ReportType_Count][MaxEntriesPerType];
|
||||||
|
os::Tick last_tick;
|
||||||
|
u32 consecutive_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
constinit RecentReportState g_state = {
|
||||||
|
.report_counts = {},
|
||||||
|
.report_entries = {},
|
||||||
|
.last_tick = os::Tick{},
|
||||||
|
.consecutive_count = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecentReport::PushEntry(const char *error_code, const char *program_id, ReportType type, bool is_system_abort, bool is_application_abort) {
|
||||||
|
u32 &count = g_state.report_counts[type];
|
||||||
|
RecentReportEntry *entries = g_state.report_entries[type];
|
||||||
|
|
||||||
|
/* If we're full, shift the oldest entry out. */
|
||||||
|
if (count >= MaxEntriesPerType) {
|
||||||
|
std::memmove(entries, entries + 1, sizeof(RecentReportEntry) * (MaxEntriesPerType - 1));
|
||||||
|
count = MaxEntriesPerType - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill the new entry. */
|
||||||
|
RecentReportEntry &entry = entries[count];
|
||||||
|
util::Strlcpy(entry.error_code, error_code, sizeof(entry.error_code));
|
||||||
|
util::Strlcpy(entry.program_id, program_id, sizeof(entry.program_id));
|
||||||
|
entry.is_visible = (type == ReportType_Visible);
|
||||||
|
entry.is_system_abort = is_system_abort;
|
||||||
|
entry.is_application_abort = is_application_abort;
|
||||||
|
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecentReport::GetSummary(RecentReportSummary *out) {
|
||||||
|
/* Fill basic info from lazily-initialized system info. */
|
||||||
|
const auto &sys_info = srv::GetSystemInfo();
|
||||||
|
util::Strlcpy(out->firmware_display_version, sys_info.os_version, sizeof(out->firmware_display_version));
|
||||||
|
util::Strlcpy(out->private_os_version, sys_info.private_os_version, sizeof(out->private_os_version));
|
||||||
|
util::Strlcpy(out->product_model, sys_info.product_model, sizeof(out->product_model));
|
||||||
|
util::Strlcpy(out->region_code, sys_info.region, sizeof(out->region_code));
|
||||||
|
|
||||||
|
u32 total_count = 0;
|
||||||
|
|
||||||
|
/* Copy entries. */
|
||||||
|
for (u32 i = 0; i < ReportType_Count; i++) {
|
||||||
|
if (g_state.report_counts[i] == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::memcpy(out->entries + total_count, g_state.report_entries[i], sizeof(RecentReportEntry) * g_state.report_counts[i]);
|
||||||
|
total_count += g_state.report_counts[i];
|
||||||
|
|
||||||
|
/* Reset count (destructive read). */
|
||||||
|
g_state.report_counts[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
out->entry_count = total_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecentReport::Clear() {
|
||||||
|
for (u32 i = 0; i < ReportType_Count; i++) {
|
||||||
|
g_state.report_counts[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::erpt::srv {
|
||||||
|
|
||||||
|
class RecentReport {
|
||||||
|
public:
|
||||||
|
static void PushEntry(const char *error_code, const char *program_id, ReportType type, bool is_system_abort, bool is_application_abort);
|
||||||
|
static void GetSummary(RecentReportSummary *out);
|
||||||
|
static void Clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@ -20,13 +20,12 @@
|
|||||||
#include "erpt_srv_context_record.hpp"
|
#include "erpt_srv_context_record.hpp"
|
||||||
#include "erpt_srv_context.hpp"
|
#include "erpt_srv_context.hpp"
|
||||||
#include "erpt_srv_fs_info.hpp"
|
#include "erpt_srv_fs_info.hpp"
|
||||||
|
#include "erpt_srv_recent_report.hpp"
|
||||||
|
|
||||||
namespace ams::erpt::srv {
|
namespace ams::erpt::srv {
|
||||||
|
|
||||||
constinit bool Reporter::s_redirect_new_reports = true;
|
constinit bool Reporter::s_redirect_new_reports = true;
|
||||||
constinit char Reporter::s_serial_number[24] = "Unknown";
|
constinit char Reporter::s_serial_number[24] = "Unknown";
|
||||||
constinit char Reporter::s_os_version[24] = "Unknown";
|
|
||||||
constinit char Reporter::s_private_os_version[96] = "Unknown";
|
|
||||||
constinit util::optional<os::Tick> Reporter::s_application_launch_time;
|
constinit util::optional<os::Tick> Reporter::s_application_launch_time;
|
||||||
constinit util::optional<os::Tick> Reporter::s_awake_time;
|
constinit util::optional<os::Tick> Reporter::s_awake_time;
|
||||||
constinit util::optional<os::Tick> Reporter::s_power_on_time;
|
constinit util::optional<os::Tick> Reporter::s_power_on_time;
|
||||||
@ -212,18 +211,83 @@ namespace ams::erpt::srv {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Result ValidateCreateReportContext(const ContextEntry *ctx) {
|
Result ValidateAndGetErrorCode(const ContextEntry *ctx, char *out_error_code) {
|
||||||
R_UNLESS(ctx->category == CategoryId_ErrorInfo, erpt::ResultRequiredContextMissing());
|
R_UNLESS(ctx->category == CategoryId_ErrorInfo, erpt::ResultRequiredContextMissing());
|
||||||
R_UNLESS(ctx->field_count <= FieldsPerContext, erpt::ResultInvalidArgument());
|
R_UNLESS(ctx->field_count <= FieldsPerContext, erpt::ResultInvalidArgument());
|
||||||
|
|
||||||
const bool found_error_code = util::range::any_of(MakeSpan(ctx->fields, ctx->field_count), [] (const FieldEntry &entry) {
|
const auto fields_span = MakeSpan(ctx->fields, ctx->field_count);
|
||||||
return entry.id == FieldId_ErrorCode;
|
const u8 *array_data = static_cast<const u8 *>(ctx->array_buffer);
|
||||||
});
|
|
||||||
R_UNLESS(found_error_code, erpt::ResultRequiredFieldMissing());
|
const FieldEntry *error_code_field = nullptr;
|
||||||
|
|
||||||
|
for (const auto &field : fields_span) {
|
||||||
|
if (field.id != FieldId_ErrorCode){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
error_code_field = &field;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
R_UNLESS(error_code_field != nullptr, erpt::ResultRequiredFieldMissing());
|
||||||
|
R_UNLESS(error_code_field->type == FieldType_String, erpt::ResultFieldTypeMismatch());
|
||||||
|
R_UNLESS(error_code_field->value_array.size <= ErrorCodeSizeMax, erpt::ResultArrayFieldTooLarge());
|
||||||
|
|
||||||
|
const char *error_code = reinterpret_cast<const char *>(array_data + error_code_field->value_array.start_idx);
|
||||||
|
util::Strlcpy(out_error_code, error_code, ErrorCodeSizeMax);
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct ThrottleState {
|
||||||
|
TimeSpan throttle_time_span;
|
||||||
|
char last_error_code[ErrorCodeSizeMax];
|
||||||
|
u32 consecutive_count;
|
||||||
|
os::Tick last_tick;
|
||||||
|
};
|
||||||
|
constinit ThrottleState g_throttle_state = {
|
||||||
|
.throttle_time_span = TimeSpan{},
|
||||||
|
.last_error_code = {},
|
||||||
|
.consecutive_count = 0,
|
||||||
|
.last_tick = os::Tick{},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
bool IsThrottledReport(const ContextEntry *ctx, ReportType type, const char *error_code) {
|
||||||
|
if (hos::GetVersion() < hos::Version_22_0_0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto fields_span = MakeSpan(ctx->fields, ctx->field_count);
|
||||||
|
bool is_crash_report = false;
|
||||||
|
|
||||||
|
for (const auto &field : fields_span) {
|
||||||
|
if (field.id != FieldId_CrashReportFlag){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
is_crash_report = field.value_bool;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(type == ReportType_Visible || is_crash_report){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto now = os::GetSystemTick();
|
||||||
|
const TimeSpan elapsed = (now - g_throttle_state.last_tick).ToTimeSpan();
|
||||||
|
|
||||||
|
if (std::strcmp(g_throttle_state.last_error_code, error_code) == 0 && elapsed < g_throttle_state.throttle_time_span) {
|
||||||
|
if (g_throttle_state.consecutive_count >= 5) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
g_throttle_state.consecutive_count++;
|
||||||
|
} else {
|
||||||
|
util::Strlcpy(g_throttle_state.last_error_code, error_code, sizeof(g_throttle_state.last_error_code));
|
||||||
|
g_throttle_state.last_tick = now;
|
||||||
|
g_throttle_state.consecutive_count = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
Result SubmitReportDefaults(const ContextEntry *ctx) {
|
Result SubmitReportDefaults(const ContextEntry *ctx) {
|
||||||
AMS_ASSERT(ctx->category == CategoryId_ErrorInfo);
|
AMS_ASSERT(ctx->category == CategoryId_ErrorInfo);
|
||||||
|
|
||||||
@ -381,6 +445,9 @@ namespace ams::erpt::srv {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Reporter::SetThrottleTimeSpan(TimeSpan time_span) {
|
||||||
|
g_throttle_state.throttle_time_span = time_span;
|
||||||
|
}
|
||||||
Result Reporter::RegisterRunningApplet(ncm::ProgramId program_id) {
|
Result Reporter::RegisterRunningApplet(ncm::ProgramId program_id) {
|
||||||
g_applet_active_time_info_list.Register(program_id);
|
g_applet_active_time_info_list.Register(program_id);
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
@ -427,12 +494,51 @@ namespace ams::erpt::srv {
|
|||||||
/* Get the context entry pointer. */
|
/* Get the context entry pointer. */
|
||||||
const ContextEntry *ctx = record->GetContextEntryPtr();
|
const ContextEntry *ctx = record->GetContextEntryPtr();
|
||||||
|
|
||||||
/* Validate the context. */
|
/* Validate the context and retrieve the error code. */
|
||||||
R_TRY(ValidateCreateReportContext(ctx));
|
char error_code[ErrorCodeSizeMax];
|
||||||
|
R_TRY(ValidateAndGetErrorCode(ctx, error_code));
|
||||||
|
|
||||||
|
if (hos::GetVersion() >= hos::Version_22_0_0) {
|
||||||
|
/* Check if we should throttle the report. */
|
||||||
|
if (IsThrottledReport(ctx, type, error_code)) {
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Submit report defaults. */
|
/* Submit report defaults. */
|
||||||
R_TRY(SubmitReportDefaults(ctx));
|
R_TRY(SubmitReportDefaults(ctx));
|
||||||
|
|
||||||
|
/* Push to recent reports. */
|
||||||
|
if (hos::GetVersion() >= hos::Version_22_0_0) {
|
||||||
|
const auto fields_span = MakeSpan(ctx->fields, ctx->field_count);
|
||||||
|
const u8 *array_data = static_cast<const u8 *>(ctx->array_buffer);
|
||||||
|
|
||||||
|
char program_id[ProgramIdSizeMax] = {};
|
||||||
|
bool is_system_abort = false;
|
||||||
|
bool is_application_abort = false;
|
||||||
|
|
||||||
|
for (const auto &field : fields_span) {
|
||||||
|
switch (field.id) {
|
||||||
|
case FieldId_ProgramId:
|
||||||
|
if(field.type != FieldType_String){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
util::Strlcpy(program_id, reinterpret_cast<const char *>(array_data + field.value_array.start_idx), sizeof(program_id));
|
||||||
|
break;
|
||||||
|
case FieldId_SystemAbortFlag:
|
||||||
|
is_system_abort = field.value_bool;
|
||||||
|
break;
|
||||||
|
case FieldId_ApplicationAbortFlag:
|
||||||
|
is_application_abort = field.value_bool;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RecentReport::PushEntry(error_code, program_id, type, is_system_abort, is_application_abort);
|
||||||
|
}
|
||||||
|
|
||||||
/* Generate report id. */
|
/* Generate report id. */
|
||||||
const ReportId report_id = specified_report_id ? *specified_report_id : ReportId{ .uuid = util::GenerateUuid() };
|
const ReportId report_id = specified_report_id ? *specified_report_id : ReportId{ .uuid = util::GenerateUuid() };
|
||||||
|
|
||||||
@ -480,8 +586,9 @@ namespace ams::erpt::srv {
|
|||||||
R_ABORT_UNLESS(time::GetStandardSteadyClockCurrentTimePoint(std::addressof(steady_clock_current_timepoint)));
|
R_ABORT_UNLESS(time::GetStandardSteadyClockCurrentTimePoint(std::addressof(steady_clock_current_timepoint)));
|
||||||
|
|
||||||
/* Add automatic fields. */
|
/* Add automatic fields. */
|
||||||
static_cast<void>(auto_record->Add(FieldId_OsVersion, s_os_version, util::Strnlen(s_os_version, sizeof(s_os_version))));
|
const auto &sys_info = srv::GetSystemInfo();
|
||||||
static_cast<void>(auto_record->Add(FieldId_PrivateOsVersion, s_private_os_version, util::Strnlen(s_private_os_version, sizeof(s_private_os_version))));
|
static_cast<void>(auto_record->Add(FieldId_OsVersion, sys_info.os_version, util::Strnlen(sys_info.os_version, sizeof(sys_info.os_version))));
|
||||||
|
static_cast<void>(auto_record->Add(FieldId_PrivateOsVersion, sys_info.private_os_version, util::Strnlen(sys_info.private_os_version, sizeof(sys_info.private_os_version))));
|
||||||
static_cast<void>(auto_record->Add(FieldId_SerialNumber, s_serial_number, util::Strnlen(s_serial_number, sizeof(s_serial_number))));
|
static_cast<void>(auto_record->Add(FieldId_SerialNumber, s_serial_number, util::Strnlen(s_serial_number, sizeof(s_serial_number))));
|
||||||
static_cast<void>(auto_record->Add(FieldId_ReportIdentifier, identifier_str, util::Strnlen(identifier_str, sizeof(identifier_str))));
|
static_cast<void>(auto_record->Add(FieldId_ReportIdentifier, identifier_str, util::Strnlen(identifier_str, sizeof(identifier_str))));
|
||||||
static_cast<void>(auto_record->Add(FieldId_OccurrenceTimestamp, timestamp_user.value));
|
static_cast<void>(auto_record->Add(FieldId_OccurrenceTimestamp, timestamp_user.value));
|
||||||
|
|||||||
@ -23,8 +23,6 @@ namespace ams::erpt::srv {
|
|||||||
private:
|
private:
|
||||||
static bool s_redirect_new_reports;
|
static bool s_redirect_new_reports;
|
||||||
static char s_serial_number[24];
|
static char s_serial_number[24];
|
||||||
static char s_os_version[24];
|
|
||||||
static char s_private_os_version[96];
|
|
||||||
static util::optional<os::Tick> s_application_launch_time;
|
static util::optional<os::Tick> s_application_launch_time;
|
||||||
static util::optional<os::Tick> s_awake_time;
|
static util::optional<os::Tick> s_awake_time;
|
||||||
static util::optional<os::Tick> s_power_on_time;
|
static util::optional<os::Tick> s_power_on_time;
|
||||||
@ -39,14 +37,11 @@ namespace ams::erpt::srv {
|
|||||||
static void UpdateAwakeTime() { s_awake_time = os::GetSystemTick(); }
|
static void UpdateAwakeTime() { s_awake_time = os::GetSystemTick(); }
|
||||||
static void UpdatePowerOnTime() { s_power_on_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) {
|
static void SetThrottleTimeSpan(TimeSpan time_span);
|
||||||
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());
|
|
||||||
|
|
||||||
|
static Result SetSerialNumber(const char *sn, u32 sn_len) {
|
||||||
|
R_UNLESS(sn_len <= sizeof(s_serial_number), erpt::ResultInvalidArgument());
|
||||||
std::memcpy(s_serial_number, sn, sn_len);
|
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);
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -40,5 +40,6 @@ namespace ams::erpt {
|
|||||||
R_DEFINE_ERROR_RESULT(InvalidPowerState, 17);
|
R_DEFINE_ERROR_RESULT(InvalidPowerState, 17);
|
||||||
R_DEFINE_ERROR_RESULT(ArrayFieldTooLarge, 18);
|
R_DEFINE_ERROR_RESULT(ArrayFieldTooLarge, 18);
|
||||||
R_DEFINE_ERROR_RESULT(AlreadyOwned, 19);
|
R_DEFINE_ERROR_RESULT(AlreadyOwned, 19);
|
||||||
|
R_DEFINE_ERROR_RESULT(TooManyOutAttachments, 51);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,26 +26,6 @@ namespace ams {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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<int>(dst_size));
|
|
||||||
case settings::system::ProductModel_Nx: return util::Strlcpy(dst, "NX", static_cast<int>(dst_size));
|
|
||||||
default: return util::SNPrintf(dst, dst_size, "%d", static_cast<int>(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";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace init {
|
namespace init {
|
||||||
@ -94,42 +74,19 @@ namespace ams {
|
|||||||
/* Atmosphere always wants to redirect new reports to the SD card, to prevent them from being logged. */
|
/* Atmosphere always wants to redirect new reports to the SD card, to prevent them from being logged. */
|
||||||
erpt::srv::SetRedirectNewReportsToSdCard(true);
|
erpt::srv::SetRedirectNewReportsToSdCard(true);
|
||||||
|
|
||||||
/* Configure the OS version. */
|
/* Configure the serial number, OS version, product model, and region. */
|
||||||
{
|
{
|
||||||
settings::system::FirmwareVersion firmware_version = {};
|
const auto &sys_info = erpt::srv::GetSystemInfo();
|
||||||
|
|
||||||
settings::system::SerialNumber serial_number = {};
|
settings::system::SerialNumber serial_number = {};
|
||||||
settings::system::GetFirmwareVersion(std::addressof(firmware_version));
|
|
||||||
settings::system::GetSerialNumber(std::addressof(serial_number));
|
settings::system::GetSerialNumber(std::addressof(serial_number));
|
||||||
|
|
||||||
char os_private[0x60];
|
R_ABORT_UNLESS(erpt::srv::SetSerialNumber(serial_number.str,
|
||||||
const auto os_priv_len = util::SNPrintf(os_private, sizeof(os_private), "%s (%.8s)", firmware_version.display_name, firmware_version.revision);
|
strnlen(serial_number.str, sizeof(serial_number.str) - 1) + 1));
|
||||||
AMS_ASSERT(static_cast<size_t>(os_priv_len) < sizeof(os_private));
|
|
||||||
AMS_UNUSED(os_priv_len);
|
|
||||||
|
|
||||||
R_ABORT_UNLESS(erpt::srv::SetSerialNumberAndOsVersion(serial_number.str,
|
R_ABORT_UNLESS(erpt::srv::SetProductModel(sys_info.product_model, static_cast<u32>(std::strlen(sys_info.product_model))));
|
||||||
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. */
|
R_ABORT_UNLESS(erpt::srv::SetRegionSetting(sys_info.region, static_cast<u32>(std::strlen(sys_info.region))));
|
||||||
{
|
|
||||||
char product_model[0x10];
|
|
||||||
const auto pm_len = erpt::MakeProductModelString(product_model, sizeof(product_model), settings::system::GetProductModel());
|
|
||||||
AMS_ASSERT(static_cast<size_t>(pm_len) < sizeof(product_model));
|
|
||||||
AMS_UNUSED(pm_len);
|
|
||||||
|
|
||||||
R_ABORT_UNLESS(erpt::srv::SetProductModel(product_model, static_cast<u32>(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<u32>(std::strlen(region_str))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Start the erpt server. */
|
/* Start the erpt server. */
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user