mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 20:31:14 +00:00
Implement blank prodinfo creation.
This is a complete implementation of what PR #532 seeks to do (thanks @ThatNerdyPikachu). However, it currently blackscreens. This is because we can't actually mitm settings, because settings must be able to complete its initialization before the sd card can be mounted. Thus we end up with a circular dependency and the console blackscreens. This problem may yet be solvable, but it's unclear immediately how this dependency might be solved. In any event, this serves as a reference to use in the event that a solution arises.
This commit is contained in:
parent
bb6cc6532b
commit
3bba035b84
4 changed files with 211 additions and 71 deletions
|
@ -32,6 +32,7 @@
|
|||
#include "fs_save_utils.hpp"
|
||||
#include "fs_subdirectory_filesystem.hpp"
|
||||
#include "fs_directory_savedata_filesystem.hpp"
|
||||
#include "fs_file_storage.hpp"
|
||||
|
||||
#include "../debug.hpp"
|
||||
|
||||
|
@ -261,11 +262,24 @@ Result FsMitmService::OpenBisStorage(Out<std::shared_ptr<IStorageInterface>> 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");
|
||||
if (bis_partition_id == BisStorageId_Boot0) {
|
||||
storage = std::make_shared<IStorageInterface>(new Boot0Storage(bis_storage, this->title_id));
|
||||
} else if (bis_partition_id == BisStorageId_Prodinfo) {
|
||||
/* PRODINFO should *never* be writable. */
|
||||
if (is_sysmodule || has_cal0_read_flag) {
|
||||
if (has_blank_cal0_flag) {
|
||||
FsFile file;
|
||||
|
||||
if (R_FAILED((rc = Utils::OpenBlankProdInfoFile(&file)))) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
storage = std::make_shared<IStorageInterface>(new FileStorage(new ProxyFile(&file)));
|
||||
if (out_storage.IsDomain()) {
|
||||
out_domain_id = file.s.object_id;
|
||||
}
|
||||
return rc;
|
||||
} else if (is_sysmodule || has_cal0_read_flag) {
|
||||
storage = std::make_shared<IStorageInterface>(new ROProxyStorage(bis_storage));
|
||||
} else {
|
||||
/* Do not allow non-sysmodules to read *or* write CAL0. */
|
||||
|
|
|
@ -44,7 +44,7 @@ class FsMitmService : public IMitmServiceObject {
|
|||
bool should_override_contents;
|
||||
public:
|
||||
FsMitmService(std::shared_ptr<Service> s, u64 pid) : IMitmServiceObject(s, pid) {
|
||||
if (Utils::HasSdDisableMitMFlag(this->title_id)) {
|
||||
if (this->title_id == TitleId_Settings || Utils::HasSdDisableMitMFlag(this->title_id)) {
|
||||
this->should_override_contents = false;
|
||||
} else {
|
||||
this->should_override_contents = (this->title_id >= TitleId_ApplicationStart || Utils::HasSdMitMFlag(this->title_id)) && Utils::HasOverrideButton(this->title_id);
|
||||
|
@ -65,7 +65,7 @@ class FsMitmService : public IMitmServiceObject {
|
|||
has_launched_qlaunch = true;
|
||||
}
|
||||
|
||||
return has_launched_qlaunch || tid == TitleId_Ns || tid >= TitleId_ApplicationStart || Utils::HasSdMitMFlag(tid);
|
||||
return has_launched_qlaunch || tid == TitleId_Settings || tid == TitleId_Ns || tid >= TitleId_ApplicationStart || Utils::HasSdMitMFlag(tid);
|
||||
}
|
||||
|
||||
static void PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx);
|
||||
|
|
|
@ -60,10 +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 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 u8 g_cal0_storage_backup[ProdInfoSize];
|
||||
static u8 g_cal0_backup[ProdInfoSize];
|
||||
|
||||
static FsFile g_blank_prodinfo_file = {0};
|
||||
|
||||
static bool IsHexadecimal(const char *str) {
|
||||
while (*str) {
|
||||
|
@ -99,7 +102,7 @@ void Utils::InitializeThreadFunc(void *args) {
|
|||
fsFsCreateDirectory(&g_sd_filesystem, "/atmosphere/automatic_backups");
|
||||
{
|
||||
FsStorage cal0_storage;
|
||||
if (R_FAILED(fsOpenBisStorage(&cal0_storage, BisStorageId_Prodinfo)) || R_FAILED(fsStorageRead(&cal0_storage, 0, g_cal0_storage_backup, ProdinfoSize))) {
|
||||
if (R_FAILED(fsOpenBisStorage(&cal0_storage, BisStorageId_Prodinfo)) || R_FAILED(fsStorageRead(&cal0_storage, 0, g_cal0_storage_backup, ProdInfoSize))) {
|
||||
std::abort();
|
||||
}
|
||||
fsStorageClose(&cal0_storage);
|
||||
|
@ -115,7 +118,7 @@ void Utils::InitializeThreadFunc(void *args) {
|
|||
snprintf(prodinfo_backup_path, sizeof(prodinfo_backup_path) - 1, "/atmosphere/automatic_backups/PRODINFO.bin");
|
||||
}
|
||||
|
||||
fsFsCreateFile(&g_sd_filesystem, prodinfo_backup_path, ProdinfoSize, 0);
|
||||
fsFsCreateFile(&g_sd_filesystem, prodinfo_backup_path, ProdInfoSize, 0);
|
||||
if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, prodinfo_backup_path, FS_OPEN_READ | FS_OPEN_WRITE, &g_cal0_file))) {
|
||||
bool has_auto_backup = false;
|
||||
size_t read = 0;
|
||||
|
@ -124,7 +127,7 @@ void Utils::InitializeThreadFunc(void *args) {
|
|||
is_cal0_valid &= memcmp(g_cal0_backup, "CAL0", 4) == 0;
|
||||
is_cal0_valid &= memcmp(g_cal0_backup + 0x250, serial_number, 0x18) == 0;
|
||||
u32 cal0_size = ((u32 *)g_cal0_backup)[2];
|
||||
is_cal0_valid &= cal0_size + 0x40 <= ProdinfoSize;
|
||||
is_cal0_valid &= cal0_size + 0x40 <= ProdInfoSize;
|
||||
if (is_cal0_valid) {
|
||||
u8 calc_hash[0x20];
|
||||
sha256CalculateHash(calc_hash, g_cal0_backup + 0x40, cal0_size);
|
||||
|
@ -134,14 +137,17 @@ void Utils::InitializeThreadFunc(void *args) {
|
|||
}
|
||||
|
||||
if (!has_auto_backup) {
|
||||
fsFileSetSize(&g_cal0_file, ProdinfoSize);
|
||||
fsFileWrite(&g_cal0_file, 0, g_cal0_storage_backup, ProdinfoSize);
|
||||
fsFileSetSize(&g_cal0_file, ProdInfoSize);
|
||||
fsFileWrite(&g_cal0_file, 0, g_cal0_storage_backup, ProdInfoSize);
|
||||
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. */
|
||||
memset(g_cal0_storage_backup, 0, sizeof(g_cal0_storage_backup));
|
||||
memset(g_cal0_backup, 0, sizeof(g_cal0_backup));
|
||||
std::memset(g_cal0_storage_backup, 0, sizeof(g_cal0_storage_backup));
|
||||
std::memset(g_cal0_backup, 0, sizeof(g_cal0_backup));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -665,3 +671,115 @@ Result Utils::GetSettingsItemBooleanValue(const char *name, const char *key, boo
|
|||
void Utils::RebootToFatalError(AtmosphereFatalErrorContext *ctx) {
|
||||
BpcRebootManager::RebootForFatalError(ctx);
|
||||
}
|
||||
|
||||
void Utils::CreateBlankProdInfo() {
|
||||
Result rc;
|
||||
|
||||
u8 *cal0;
|
||||
if (IsCal0Valid(g_cal0_backup)) {
|
||||
cal0 = g_cal0_backup;
|
||||
} else if (IsCal0Valid(g_cal0_storage_backup)) {
|
||||
cal0 = g_cal0_storage_backup;
|
||||
} else {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
const char blank_serial[] = "XAW00000000000";
|
||||
std::memcpy(&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]);
|
||||
}
|
||||
|
||||
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<u32 *>(&cal0[0xAD0]), *reinterpret_cast<u32 *>(&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));
|
||||
}
|
||||
}
|
||||
|
||||
/* 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();
|
||||
}
|
||||
}
|
||||
|
||||
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 &= 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;
|
||||
}
|
||||
|
||||
return is_cal0_valid;
|
||||
}
|
||||
|
||||
u16 Utils::GetCrc16(const void *data, size_t size) {
|
||||
static constexpr u16 s_crc_table[0x10] = {
|
||||
0x0000, 0xCC01, 0xD801, 0x1400, 0xF001, 0x3C00, 0x2800, 0xE401,
|
||||
0xA001, 0x6C00, 0x7800, 0xB401, 0x5000, 0x9C01, 0x8801, 0x4400
|
||||
};
|
||||
|
||||
if (data == nullptr) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
u16 crc16 = 0x55AA;
|
||||
const u8 *data_u8 = reinterpret_cast<const u8 *>(data);
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
crc16 = (crc16 >> 4) ^ (s_crc_table[crc16 & 0xF]) ^ (s_crc_table[data_u8[i] & 0xF]);
|
||||
crc16 = (crc16 >> 4) ^ (s_crc_table[crc16 & 0xF]) ^ (s_crc_table[(data_u8[i] >> 4) & 0xF]);
|
||||
}
|
||||
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;
|
||||
}
|
|
@ -61,6 +61,8 @@ class Utils {
|
|||
|
||||
static bool HasSdRomfsContent(u64 title_id);
|
||||
|
||||
static Result OpenBlankProdInfoFile(FsFile *out);
|
||||
|
||||
/* Delayed Initialization + MitM detection. */
|
||||
static void InitializeThreadFunc(void *args);
|
||||
|
||||
|
@ -88,8 +90,14 @@ class Utils {
|
|||
|
||||
static Result GetSettingsItemBooleanValue(const char *name, const char *key, bool *out);
|
||||
|
||||
/* CRC util. */
|
||||
static u16 GetCrc16(const void *data, size_t size);
|
||||
|
||||
/* Error occurred. */
|
||||
static void RebootToFatalError(AtmosphereFatalErrorContext *ctx);
|
||||
private:
|
||||
static void RefreshConfiguration();
|
||||
|
||||
static void CreateBlankProdInfo();
|
||||
static bool IsCal0Valid(const u8 *cal0);
|
||||
};
|
Loading…
Reference in a new issue