diff --git a/exosphere/src/configitem.c b/exosphere/src/configitem.c
index b53421d6f..233e2c772 100644
--- a/exosphere/src/configitem.c
+++ b/exosphere/src/configitem.c
@@ -282,7 +282,11 @@ uint32_t configitem_get(bool privileged, ConfigItem item, uint64_t *p_outvalue)
break;
case CONFIGITEM_HAS_RCM_BUG_PATCH:
/* UNOFFICIAL: Gets whether this unit has the RCM bug patched. */
- *p_outvalue = (int)(fuse_has_rcm_bug_patch());;
+ *p_outvalue = (int)(fuse_has_rcm_bug_patch());
+ break;
+ case CONFIGITEM_SHOULD_BLANK_PROD_INFO:
+ /* UNOFFICIAL: Gets whether we should blank out certain parts of PRODINFO. */
+ *p_outvalue = (int)(exosphere_should_blank_prod_info() != 0);
break;
default:
result = 2;
diff --git a/exosphere/src/configitem.h b/exosphere/src/configitem.h
index b9600c833..6000ac44e 100644
--- a/exosphere/src/configitem.h
+++ b/exosphere/src/configitem.h
@@ -45,6 +45,7 @@ typedef enum {
CONFIGITEM_NEEDS_SHUTDOWN = 65002,
CONFIGITEM_EXOSPHERE_VERHASH = 65003,
CONFIGITEM_HAS_RCM_BUG_PATCH = 65004,
+ CONFIGITEM_SHOULD_BLANK_PROD_INFO = 65005,
} ConfigItem;
#define REBOOT_KIND_NO_REBOOT 0
diff --git a/exosphere/src/exocfg.c b/exosphere/src/exocfg.c
index 533de8beb..9705098f8 100644
--- a/exosphere/src/exocfg.c
+++ b/exosphere/src/exocfg.c
@@ -83,3 +83,11 @@ unsigned int exosphere_should_disable_usermode_exception_handlers(void) {
return EXOSPHERE_CHECK_FLAG(EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS);
}
+
+unsigned int exosphere_should_blank_prod_info(void) {
+ if (!g_has_loaded_config) {
+ generic_panic();
+ }
+
+ return EXOSPHERE_CHECK_FLAG(EXOSPHERE_FLAG_SHOULD_BLANK_PRODINFO);
+}
diff --git a/exosphere/src/exocfg.h b/exosphere/src/exocfg.h
index ba53be403..b1dc2f927 100644
--- a/exosphere/src/exocfg.h
+++ b/exosphere/src/exocfg.h
@@ -39,6 +39,7 @@
#define EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV (1 << 1u)
#define EXOSPHERE_FLAG_IS_DEBUGMODE_USER (1 << 2u)
#define EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS (1 << 3u)
+#define EXOSPHERE_FLAG_SHOULD_BLANK_PRODINFO (1 << 4u)
#define EXOSPHERE_FLAGS_DEFAULT (EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV)
typedef struct {
@@ -54,6 +55,7 @@ unsigned int exosphere_should_perform_620_keygen(void);
unsigned int exosphere_should_override_debugmode_priv(void);
unsigned int exosphere_should_override_debugmode_user(void);
unsigned int exosphere_should_disable_usermode_exception_handlers(void);
+unsigned int exosphere_should_blank_prod_info(void);
static inline unsigned int exosphere_get_target_firmware_for_init(void) {
const unsigned int magic = MAILBOX_EXOSPHERE_CONFIG_PHYS.magic;
diff --git a/fusee/fusee-secondary/src/exocfg.h b/fusee/fusee-secondary/src/exocfg.h
index e8054ec9d..219509785 100644
--- a/fusee/fusee-secondary/src/exocfg.h
+++ b/fusee/fusee-secondary/src/exocfg.h
@@ -28,6 +28,7 @@
#define EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV (1 << 1u)
#define EXOSPHERE_FLAG_IS_DEBUGMODE_USER (1 << 2u)
#define EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS (1 << 3u)
+#define EXOSPHERE_FLAG_SHOULD_BLANK_PRODINFO (1 << 4u)
#define EXOSPHERE_FLAGS_DEFAULT (EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV)
typedef struct {
diff --git a/fusee/fusee-secondary/src/nxboot.c b/fusee/fusee-secondary/src/nxboot.c
index f7badf580..bc161b4ba 100644
--- a/fusee/fusee-secondary/src/nxboot.c
+++ b/fusee/fusee-secondary/src/nxboot.c
@@ -199,6 +199,14 @@ static void nxboot_configure_exosphere(uint32_t target_firmware, unsigned int ke
fatal_error("[NXBOOT]: Failed to parse BCT.ini!\n");
}
+ {
+ FILE *flag = fopen("atmosphere/flags/blank_prodinfo.flag", "rb");
+ if (flag != NULL) {
+ exo_cfg.flags |= EXOSPHERE_FLAG_SHOULD_BLANK_PRODINFO;
+ fclose(flag);
+ }
+ }
+
if ((exo_cfg.target_firmware < ATMOSPHERE_TARGET_FIRMWARE_MIN) || (exo_cfg.target_firmware > ATMOSPHERE_TARGET_FIRMWARE_MAX)) {
fatal_error("[NXBOOT]: Invalid Exosphere target firmware!\n");
}
diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_istorage.hpp b/stratosphere/ams_mitm/source/fs_mitm/fs_istorage.hpp
index 34ace2eb9..655b356c8 100644
--- a/stratosphere/ams_mitm/source/fs_mitm/fs_istorage.hpp
+++ b/stratosphere/ams_mitm/source/fs_mitm/fs_istorage.hpp
@@ -112,7 +112,6 @@ class IROStorage : public IStorage {
virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) = 0;
};
-
class ProxyStorage : public IStorage {
private:
FsStorage *base_storage;
@@ -172,4 +171,4 @@ class ROProxyStorage : public IROStorage {
virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override {
return fsStorageOperateRange(this->base_storage, operation_type, offset, size, out_range_info);
};
-};
\ No newline at end of file
+};
diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_memory_storage.hpp b/stratosphere/ams_mitm/source/fs_mitm/fs_memory_storage.hpp
new file mode 100644
index 000000000..a195f6dd2
--- /dev/null
+++ b/stratosphere/ams_mitm/source/fs_mitm/fs_memory_storage.hpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2018-2019 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 "fs_shim.h"
+
+#include "../debug.hpp"
+#include "fs_istorage.hpp"
+
+class MemoryStorage : public IStorage {
+ private:
+ u8 * const buffer;
+ const u64 size;
+ public:
+ MemoryStorage(void *b, u64 sz) : buffer(static_cast(b)), size(sz) {
+ /* ... */
+ }
+
+ virtual ~MemoryStorage() {
+ /* ... */
+ }
+
+ public:
+ virtual Result Read(void *buffer, size_t size, u64 offset) override {
+ if (size == 0) {
+ return ResultSuccess;
+ }
+ if (buffer == nullptr) {
+ return ResultFsNullptrArgument;
+ }
+ if (!IStorage::IsRangeValid(offset, size, this->size)) {
+ return ResultFsOutOfRange;
+ }
+
+ std::memcpy(buffer, this->buffer + offset, size);
+ return ResultSuccess;
+ }
+
+ virtual Result Write(void *buffer, size_t size, u64 offset) override {
+ if (size == 0) {
+ return ResultSuccess;
+ }
+ if (buffer == nullptr) {
+ return ResultFsNullptrArgument;
+ }
+ if (!IStorage::IsRangeValid(offset, size, this->size)) {
+ return ResultFsOutOfRange;
+ }
+
+ std::memcpy(this->buffer + offset, buffer, size);
+ return ResultSuccess;
+ }
+
+ virtual Result Flush() override {
+ return ResultSuccess;
+ }
+
+ virtual Result GetSize(u64 *out_size) override {
+ *out_size = this->size;
+ return ResultSuccess;
+ }
+
+ virtual Result SetSize(u64 size) override {
+ return ResultFsUnsupportedOperation;
+ }
+
+ virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override {
+ switch (operation_type) {
+ case 2: /* TODO: OperationType_Invalidate */
+ return ResultSuccess;
+ case 3: /* TODO: OperationType_Query */
+ if (out_range_info == nullptr) {
+ return ResultFsNullptrArgument;
+ }
+ /* N checks for size == sizeof(*out_range_info) here, but that's because their wrapper api is bad. */
+ std::memset(out_range_info, 0, sizeof(*out_range_info));
+ return ResultSuccess;
+ default:
+ return ResultFsUnsupportedOperation;
+ }
+ }
+};
+
+class ReadOnlyMemoryStorage : public MemoryStorage {
+ public:
+ ReadOnlyMemoryStorage(const void *b, u64 sz) : MemoryStorage(const_cast(b), sz) {
+ /* ... */
+ }
+
+ virtual ~ReadOnlyMemoryStorage() {
+ /* ... */
+ }
+
+ public:
+ virtual Result Write(void *buffer, size_t size, u64 offset) override {
+ return ResultFsUnsupportedOperation;
+ }
+};
diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp
index aa4f719b1..aaa40cbda 100644
--- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp
+++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp
@@ -27,12 +27,14 @@
#include "fsmitm_boot0storage.hpp"
#include "fsmitm_romstorage.hpp"
#include "fsmitm_layeredrom.hpp"
+#include "fsmitm_utils.hpp"
#include "fs_dir_utils.hpp"
#include "fs_save_utils.hpp"
#include "fs_subdirectory_filesystem.hpp"
#include "fs_directory_savedata_filesystem.hpp"
#include "fs_file_storage.hpp"
+#include "fs_memory_storage.hpp"
#include "../debug.hpp"
@@ -262,23 +264,13 @@ Result FsMitmService::OpenBisStorage(Out> out
const bool is_sysmodule = TitleIdIsSystem(this->title_id);
const bool has_bis_write_flag = Utils::HasFlag(this->title_id, "bis_write");
const bool has_cal0_read_flag = Utils::HasFlag(this->title_id, "cal_read");
- const bool has_blank_cal0_flag = Utils::HasGlobalFlag("blank_prodinfo");
+ const bool has_blank_cal0_flag = ShouldBlankProdInfo();
if (bis_partition_id == BisStorageId_Boot0) {
storage = std::make_shared(new Boot0Storage(bis_storage, this->title_id));
} else if (bis_partition_id == BisStorageId_Prodinfo) {
/* PRODINFO should *never* be writable. */
if (has_blank_cal0_flag) {
- FsFile file;
-
- if (R_FAILED((rc = Utils::OpenBlankProdInfoFile(&file)))) {
- return rc;
- }
-
- storage = std::make_shared(new FileStorage(new ProxyFile(&file)));
- if (out_storage.IsDomain()) {
- out_domain_id = file.s.object_id;
- }
- return rc;
+ storage = std::make_shared(new MitmProxyStorage(std::make_unique(Utils::GetBlankProdInfoBuffer(), ProdInfoSize), bis_storage.s));
} else if (is_sysmodule || has_cal0_read_flag) {
storage = std::make_shared(new ROProxyStorage(bis_storage));
} else {
diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_utils.hpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_utils.hpp
new file mode 100644
index 000000000..abca5702a
--- /dev/null
+++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_utils.hpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2018-2019 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 "fs_istorage.hpp"
+
+class MitmProxyStorage : public IStorage {
+ private:
+ Service srv_holder;
+ std::unique_ptr fwd_storage;
+ public:
+ MitmProxyStorage(std::unique_ptr st, Service sr = {}) : srv_holder(sr), fwd_storage(std::move(st)) {
+ /* ... */
+ }
+ virtual ~MitmProxyStorage() {
+ if (serviceIsActive(&srv_holder)) {
+ serviceClose(&srv_holder);
+ }
+ }
+ public:
+ virtual Result Read(void *buffer, size_t size, u64 offset) override {
+ return this->fwd_storage->Read(buffer, size, offset);
+ }
+
+ virtual Result Write(void *buffer, size_t size, u64 offset) override {
+ return this->fwd_storage->Write(buffer, size, offset);
+ }
+
+ virtual Result Flush() override {
+ return this->fwd_storage->Flush();
+ }
+
+ virtual Result GetSize(u64 *out_size) override {
+ return this->fwd_storage->GetSize(out_size);
+ }
+
+ virtual Result SetSize(u64 size) override {
+ return this->fwd_storage->SetSize(size);
+ }
+
+ virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override {
+ return this->fwd_storage->OperateRange(operation_type, offset, size, out_range_info);
+ }
+};
diff --git a/stratosphere/ams_mitm/source/utils.cpp b/stratosphere/ams_mitm/source/utils.cpp
index 5a0f6f928..e19e4b260 100644
--- a/stratosphere/ams_mitm/source/utils.cpp
+++ b/stratosphere/ams_mitm/source/utils.cpp
@@ -60,13 +60,13 @@ static HblOverrideConfig g_hbl_override_config = {
static char g_config_ini_data[0x800];
/* Backup file for CAL0 partition. */
-static constexpr size_t ProdInfoSize = 0x8000;
-static constexpr const char *BlankProdInfoPath = "/atmosphere/automatic_backups/BLANK_PRODINFO.bin";
static FsFile g_cal0_file = {0};
static u8 g_cal0_storage_backup[ProdInfoSize];
static u8 g_cal0_backup[ProdInfoSize];
-static FsFile g_blank_prodinfo_file = {0};
+static HosMutex g_blank_cal0_lock;
+static bool g_initialized_blank_cal0 = false;
+static u8 g_blank_cal0[ProdInfoSize];
static bool IsHexadecimal(const char *str) {
while (*str) {
@@ -142,9 +142,6 @@ void Utils::InitializeThreadFunc(void *args) {
fsFileFlush(&g_cal0_file);
}
- /* Use one of the storages to make a blank prodinfo file. */
- Utils::CreateBlankProdInfo();
-
/* NOTE: g_cal0_file is intentionally not closed here. This prevents any other process from opening it. */
std::memset(g_cal0_storage_backup, 0, sizeof(g_cal0_storage_backup));
std::memset(g_cal0_backup, 0, sizeof(g_cal0_backup));
@@ -672,81 +669,60 @@ void Utils::RebootToFatalError(AtmosphereFatalErrorContext *ctx) {
BpcRebootManager::RebootForFatalError(ctx);
}
-void Utils::CreateBlankProdInfo() {
- Result rc;
+void Utils::EnsureBlankProdInfo() {
+ std::scoped_lock lk(g_blank_cal0_lock);
+ if (g_initialized_blank_cal0) {
+ return;
+ }
- u8 *cal0;
- if (IsCal0Valid(g_cal0_backup)) {
- cal0 = g_cal0_backup;
- } else if (IsCal0Valid(g_cal0_storage_backup)) {
- cal0 = g_cal0_storage_backup;
- } else {
+ /* Read CAL0 in from NAND. */
+ {
+ FsStorage cal0_storage;
+ if (R_FAILED(fsOpenBisStorage(&cal0_storage, BisStorageId_Prodinfo)) || R_FAILED(fsStorageRead(&cal0_storage, 0, g_blank_cal0, ProdInfoSize))) {
+ std::abort();
+ }
+ fsStorageClose(&cal0_storage);
+ }
+
+ if (!IsCal0Valid(g_blank_cal0)) {
std::abort();
}
const char blank_serial[] = "XAW00000000000";
- std::memcpy(&cal0[0x250], blank_serial, sizeof(blank_serial) - 1);
+ std::memcpy(&g_blank_cal0[0x250], blank_serial, sizeof(blank_serial) - 1);
static constexpr size_t NumErase = 7;
for (size_t i = 0; i < NumErase; i++) {
static constexpr size_t s_erase_offsets[NumErase] = {0xAE0, 0x3AE0, 0x35E1, 0x36E1, 0x2B0, 0x3D70, 0x3FC0};
static constexpr size_t s_erase_sizes[NumErase] = {0x800, 0x130, 0x6, 0x6, 0x180, 0x240, 0x240};
- std::memset(&cal0[s_erase_offsets[i]], 0, s_erase_sizes[i]);
+ std::memset(&g_blank_cal0[s_erase_offsets[i]], 0, s_erase_sizes[i]);
}
static constexpr size_t NumHashes = 2;
{
static constexpr size_t s_hash_offsets[NumHashes] = {0x12E0, 0x20};
static constexpr size_t s_data_offsets[NumHashes] = {0xAE0, 0x40};
- const size_t data_sizes[NumHashes] = {*reinterpret_cast(&cal0[0xAD0]), *reinterpret_cast(&cal0[0x8])};
+ const size_t data_sizes[NumHashes] = {*reinterpret_cast(&g_blank_cal0[0xAD0]), *reinterpret_cast(&g_blank_cal0[0x8])};
for (size_t i = 0; i < NumHashes; i++) {
u8 hash[SHA256_HASH_SIZE];
- sha256CalculateHash(hash, &cal0[s_data_offsets[i]], data_sizes[i]);
- std::memcpy(&cal0[s_hash_offsets[i]], hash, sizeof(hash));
+ sha256CalculateHash(hash, &g_blank_cal0[s_data_offsets[i]], data_sizes[i]);
+ std::memcpy(&g_blank_cal0[s_hash_offsets[i]], hash, sizeof(hash));
}
}
- /* File creation is allowed to fail, because it may already exist. */
- fsFsCreateFile(&g_sd_filesystem, BlankProdInfoPath, ProdInfoSize, 0);
-
- FsFile f;
- if (R_FAILED((rc = fsFsOpenFile(&g_sd_filesystem, BlankProdInfoPath, FS_OPEN_READ | FS_OPEN_WRITE, &f)))) {
- std::abort();
- }
-
- if (R_FAILED((rc = fsFileSetSize(&f, ProdInfoSize)))) {
- std::abort();
- }
-
- if (R_FAILED((rc = fsFileWrite(&f, 0, cal0, ProdInfoSize)))) {
- std::abort();
- }
-
- if (R_FAILED((rc = fsFileFlush(&f)))) {
- std::abort();
- }
-
- fsFileClose(&f);
-
- if (R_FAILED((rc = fsFsOpenFile(&g_sd_filesystem, BlankProdInfoPath, FS_OPEN_READ, &g_blank_prodinfo_file)))) {
- std::abort();
- }
+ g_initialized_blank_cal0 = true;
}
bool Utils::IsCal0Valid(const u8 *cal0) {
- char serial_number[0x40] = {0};
- std::memcpy(serial_number, cal0 + 0x250, 0x18);
-
bool is_cal0_valid = true;
- is_cal0_valid &= std::memcmp(g_cal0_backup, "CAL0", 4) == 0;
- is_cal0_valid &= std::memcmp(g_cal0_backup + 0x250, serial_number, 0x18) == 0;
- u32 cal0_size = ((u32 *)g_cal0_backup)[2];
+ is_cal0_valid &= std::memcmp(cal0, "CAL0", 4) == 0;
+ u32 cal0_size = ((u32 *)cal0)[2];
is_cal0_valid &= cal0_size + 0x40 <= ProdInfoSize;
if (is_cal0_valid) {
u8 calc_hash[0x20];
- sha256CalculateHash(calc_hash, g_cal0_backup + 0x40, cal0_size);
- is_cal0_valid &= std::memcmp(calc_hash, g_cal0_backup + 0x20, sizeof(calc_hash)) == 0;
+ sha256CalculateHash(calc_hash, cal0 + 0x40, cal0_size);
+ is_cal0_valid &= std::memcmp(calc_hash, cal0 + 0x20, sizeof(calc_hash)) == 0;
}
return is_cal0_valid;
@@ -771,15 +747,7 @@ u16 Utils::GetCrc16(const void *data, size_t size) {
return crc16;
}
-Result Utils::OpenBlankProdInfoFile(FsFile *out) {
- if (!IsSdInitialized()) {
- std::abort();
- return ResultFsSdCardNotPresent;
- }
-
- Result rc = OpenSdFile(BlankProdInfoPath, FS_OPEN_READ, out);
- if (R_FAILED((rc))) {
- std::abort();
- }
- return rc;
-}
\ No newline at end of file
+const void *Utils::GetBlankProdInfoBuffer() {
+ EnsureBlankProdInfo();
+ return g_blank_cal0;
+}
diff --git a/stratosphere/ams_mitm/source/utils.hpp b/stratosphere/ams_mitm/source/utils.hpp
index 51f8cfe84..b4a0c7a1c 100644
--- a/stratosphere/ams_mitm/source/utils.hpp
+++ b/stratosphere/ams_mitm/source/utils.hpp
@@ -37,6 +37,8 @@ enum BisStorageId : u32 {
BisStorageId_SystemProperPartition = 33,
};
+static constexpr size_t ProdInfoSize = 0x8000;
+
struct OverrideKey {
u64 key_combination;
bool override_by_default;
@@ -61,7 +63,7 @@ class Utils {
static bool HasSdRomfsContent(u64 title_id);
- static Result OpenBlankProdInfoFile(FsFile *out);
+ static const void *GetBlankProdInfoBuffer();
/* Delayed Initialization + MitM detection. */
static void InitializeThreadFunc(void *args);
@@ -98,6 +100,6 @@ class Utils {
private:
static void RefreshConfiguration();
- static void CreateBlankProdInfo();
+ static void EnsureBlankProdInfo();
static bool IsCal0Valid(const u8 *cal0);
};
\ No newline at end of file
diff --git a/stratosphere/libstratosphere b/stratosphere/libstratosphere
index 8ec43f0d6..5fe1dacee 160000
--- a/stratosphere/libstratosphere
+++ b/stratosphere/libstratosphere
@@ -1 +1 @@
-Subproject commit 8ec43f0d69463784c00ddb19c5f3b043e713548e
+Subproject commit 5fe1dacee28265af5ed8272a3c9921904d6f50fe