fs.mitm: Implement bis protection
This commit is contained in:
80
stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp
Normal file
80
stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "fs_shim.h"
|
||||
#include "fs_mitm_service.hpp"
|
||||
#include "fsmitm_boot0storage.hpp"
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
using namespace ams::fs;
|
||||
|
||||
namespace {
|
||||
|
||||
bool GetSettingsItemBooleanValue(const char *name, const char *key) {
|
||||
u8 tmp = 0;
|
||||
AMS_ASSERT(settings::fwdbg::GetSettingsItemValue(&tmp, sizeof(tmp), name, key) == sizeof(tmp));
|
||||
return (tmp != 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result FsMitmService::OpenBisStorage(sf::Out<std::shared_ptr<IStorageInterface>> out, u32 _bis_partition_id) {
|
||||
const ::FsBisPartitionId bis_partition_id = static_cast<::FsBisPartitionId>(_bis_partition_id);
|
||||
|
||||
/* Try to open a storage for the partition. */
|
||||
FsStorage bis_storage;
|
||||
R_TRY(fsOpenBisStorageFwd(this->forward_service.get(), &bis_storage, bis_partition_id));
|
||||
const sf::cmif::DomainObjectId target_object_id{bis_storage.s.object_id};
|
||||
|
||||
const bool is_sysmodule = ncm::IsSystemProgramId(this->client_info.program_id);
|
||||
const bool is_hbl = this->client_info.override_status.IsHbl();
|
||||
const bool can_write_bis = is_sysmodule || (is_hbl && GetSettingsItemBooleanValue("atmosphere", "enable_hbl_bis_write"));
|
||||
const bool can_read_cal = is_sysmodule || (is_hbl && GetSettingsItemBooleanValue("atmosphere", "enable_hbl_cal_read"));
|
||||
|
||||
/* Allow HBL to write to boot1 (safe firm) + package2. */
|
||||
/* This is needed to not break compatibility with ChoiDujourNX, which does not check for write access before beginning an update. */
|
||||
/* TODO: get fixed so that this can be turned off without causing bricks :/ */
|
||||
const bool is_package2 = (FsBisPartitionId_BootConfigAndPackage2Part1 <= bis_partition_id && bis_partition_id <= FsBisPartitionId_BootConfigAndPackage2Part6);
|
||||
const bool is_boot1 = bis_partition_id == FsBisPartitionId_BootPartition2Root;
|
||||
const bool can_write_bis_for_choi_support = is_hbl && (is_package2 || is_boot1);
|
||||
|
||||
/* Set output storage. */
|
||||
if (bis_partition_id == FsBisPartitionId_BootPartition1Root) {
|
||||
out.SetValue(std::make_shared<IStorageInterface>(new Boot0Storage(bis_storage, this->client_info)), target_object_id);
|
||||
} else if (bis_partition_id == FsBisPartitionId_CalibrationBinary) {
|
||||
/* PRODINFO should *never* be writable. */
|
||||
/* If we have permissions, create a read only storage. */
|
||||
if (can_read_cal) {
|
||||
out.SetValue(std::make_shared<IStorageInterface>(new ReadOnlyStorageAdapter(new RemoteStorage(bis_storage))), target_object_id);
|
||||
} else {
|
||||
/* If we can't read cal, return permission denied. */
|
||||
fsStorageClose(&bis_storage);
|
||||
return fs::ResultPermissionDenied();
|
||||
}
|
||||
} else {
|
||||
if (can_write_bis || can_write_bis_for_choi_support) {
|
||||
/* We can write, so create a writable storage. */
|
||||
out.SetValue(std::make_shared<IStorageInterface>(new RemoteStorage(bis_storage)), target_object_id);
|
||||
} else {
|
||||
/* We can only read, so create a readable storage. */
|
||||
out.SetValue(std::make_shared<IStorageInterface>(new ReadOnlyStorageAdapter(new RemoteStorage(bis_storage))), target_object_id);
|
||||
}
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,26 +16,63 @@
|
||||
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsmitm_istorage_interface.hpp"
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
/* TODO: Consider re-enabling the mitm flag logic. */
|
||||
|
||||
class FsMitmService : public sf::IMitmServiceObject {
|
||||
private:
|
||||
enum class CommandId {
|
||||
/* TODO */
|
||||
OpenFileSystemDeprecated = 0,
|
||||
|
||||
SetCurrentProcess = 1,
|
||||
OpenFileSystemWithPatch = 7,
|
||||
OpenFileSystemWithId = 8,
|
||||
|
||||
OpenSdCardFileSystem = 18,
|
||||
|
||||
OpenSaveDataFileSystem = 51,
|
||||
|
||||
OpenBisStorage = 12,
|
||||
OpenDataStorageByCurrentProcess = 200,
|
||||
OpenDataStorageByDataId = 202,
|
||||
};
|
||||
public:
|
||||
static bool ShouldMitm(const sm::MitmProcessInfo &client_info) {
|
||||
/* TODO */
|
||||
return false;
|
||||
static std::atomic_bool has_launched_qlaunch = false;
|
||||
|
||||
/* TODO: intercepting everything seems to cause issues with sleep mode, for some reason. */
|
||||
/* Figure out why, and address it. */
|
||||
/* TODO: This may be because pre-rewrite code really mismanaged domain objects in a way that would cause bad things. */
|
||||
/* Need to verify if this is fixed now. */
|
||||
if (client_info.program_id == ncm::ProgramId::AppletQlaunch || client_info.program_id == ncm::ProgramId::AppletMaintenanceMenu) {
|
||||
has_launched_qlaunch = true;
|
||||
}
|
||||
|
||||
return has_launched_qlaunch || client_info.program_id == ncm::ProgramId::Ns || !ncm::IsSystemProgramId(client_info.program_id);
|
||||
}
|
||||
public:
|
||||
SF_MITM_SERVICE_OBJECT_CTOR(FsMitmService) { /* ... */ }
|
||||
protected:
|
||||
/* TODO */
|
||||
/* Overridden commands. */
|
||||
/* Result OpenFileSystemWithPatch(Out<std::shared_ptr<IFileSystemInterface>> out, u64 program_id, u32 filesystem_type); */
|
||||
/* Result OpenFileSystemWithId(Out<std::shared_ptr<IFileSystemInterface>> out, InPointer<char> path, u64 program_id, u32 filesystem_type); */
|
||||
/* Result OpenSdCardFileSystem(Out<std::shared_ptr<IFileSystemInterface>> out); */
|
||||
/* Result OpenSaveDataFileSystem(Out<std::shared_ptr<IFileSystemInterface>> out, u8 space_id, FsSave save_struct); */
|
||||
Result OpenBisStorage(sf::Out<std::shared_ptr<IStorageInterface>> out, u32 bis_partition_id);
|
||||
/* Result OpenDataStorageByCurrentProcess(Out<std::shared_ptr<IStorageInterface>> out); */
|
||||
/* Result OpenDataStorageByDataId(Out<std::shared_ptr<IStorageInterface>> out, u64 data_id, u8 storage_id); */
|
||||
public:
|
||||
DEFINE_SERVICE_DISPATCH_TABLE {
|
||||
/* TODO */
|
||||
/* MAKE_SERVICE_COMMAND_META(OpenFileSystemWithPatch, hos::Version_200), */
|
||||
/* MAKE_SERVICE_COMMAND_META(OpenFileSystemWithId, hos::Version_200), */
|
||||
/* MAKE_SERVICE_COMMAND_META(OpenSdCardFileSystem), */
|
||||
/* MAKE_SERVICE_COMMAND_META(OpenSaveDataFileSystem), */
|
||||
MAKE_SERVICE_COMMAND_META(OpenBisStorage),
|
||||
/* MAKE_SERVICE_COMMAND_META(OpenDataStorageByCurrentProcess), */
|
||||
/* MAKE_SERVICE_COMMAND_META(OpenDataStorageByDataId), */
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
25
stratosphere/ams_mitm/source/fs_mitm/fs_shim.c
Normal file
25
stratosphere/ams_mitm/source/fs_mitm/fs_shim.c
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "fs_shim.h"
|
||||
|
||||
/* Missing fsp-srv commands. */
|
||||
Result fsOpenBisStorageFwd(Service* s, FsStorage* out, FsBisPartitionId partition_id) {
|
||||
const u32 tmp = partition_id;
|
||||
return serviceDispatchIn(s, 12, tmp,
|
||||
.out_num_objects = 1,
|
||||
.out_objects = &out->s,
|
||||
);
|
||||
}
|
||||
19
stratosphere/ams_mitm/source/fs_mitm/fs_shim.h
Normal file
19
stratosphere/ams_mitm/source/fs_mitm/fs_shim.h
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @file fs_shim.h
|
||||
* @brief Filesystem Services (fs) IPC wrapper for fs.mitm.
|
||||
* @author SciresM
|
||||
* @copyright libnx Authors
|
||||
*/
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Missing fsp-srv commands. */
|
||||
Result fsOpenBisStorageFwd(Service* s, FsStorage* out, FsBisPartitionId partition_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
120
stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.cpp
Normal file
120
stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "fsmitm_boot0storage.hpp"
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
using namespace ams::fs;
|
||||
|
||||
namespace {
|
||||
|
||||
os::Mutex g_boot0_access_mutex;
|
||||
u8 g_boot0_bct_buffer[Boot0Storage::BctEndOffset];
|
||||
|
||||
}
|
||||
|
||||
bool Boot0Storage::CanModifyBctPublicKey() {
|
||||
if (exosphere::IsRcmBugPatched()) {
|
||||
/* RCM bug patched. */
|
||||
/* Only allow NS to update the BCT pubks. */
|
||||
/* AutoRCM on a patched unit will cause a brick, so homebrew should NOT be allowed to write. */
|
||||
return this->client_info.program_id == ncm::ProgramId::Ns;
|
||||
} else {
|
||||
/* RCM bug unpatched. */
|
||||
/* Allow homebrew but not NS to update the BCT pubks. */
|
||||
return this->client_info.override_status.IsHbl();
|
||||
}
|
||||
}
|
||||
|
||||
Result Boot0Storage::Read(s64 offset, void *_buffer, size_t size) {
|
||||
std::scoped_lock lk{g_boot0_access_mutex};
|
||||
|
||||
/* Check if we have nothing to do. */
|
||||
if (size == 0) {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
return Base::Read(offset, _buffer, size);
|
||||
}
|
||||
|
||||
Result Boot0Storage::Write(s64 offset, const void *_buffer, size_t size) {
|
||||
std::scoped_lock lk{g_boot0_access_mutex};
|
||||
|
||||
const u8 *buffer = static_cast<const u8 *>(_buffer);
|
||||
|
||||
/* Check if we have nothing to do. */
|
||||
if (size == 0) {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
/* Protect the EKS region from writes. */
|
||||
if (offset <= EksStart) {
|
||||
if (offset + size < EksStart) {
|
||||
/* Fall through, no need to do anything here. */
|
||||
} else {
|
||||
if (offset + size > EksEnd) {
|
||||
/* Perform portion of write falling past end of keyblobs. */
|
||||
const s64 diff = EksEnd - offset;
|
||||
R_TRY(Base::Write(EksEnd, buffer + diff, size - diff));
|
||||
}
|
||||
/* Adjust size to avoid writing end of data. */
|
||||
size = EksStart - offset;
|
||||
}
|
||||
} else if (offset < EksEnd) {
|
||||
if (offset + size <= EksEnd) {
|
||||
/* Ignore writes falling strictly within the region. */
|
||||
return ResultSuccess();
|
||||
} else {
|
||||
/* Only write past the end of the region. */
|
||||
const s64 diff = EksEnd - offset;
|
||||
buffer += diff;
|
||||
size -= diff;
|
||||
offset = EksEnd;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we have nothing to write, succeed immediately. */
|
||||
if (size == 0) {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
/* We want to protect AutoRCM from NS on ipatched units. If we can modify bct pubks or we're not touching any of them, proceed. */
|
||||
if (this->CanModifyBctPublicKey() || offset >= BctEndOffset || (util::AlignUp(offset, BctSize) >= BctEndOffset && (offset % BctSize) >= BctPubkEnd)) {
|
||||
return Base::Write(offset, buffer, size);
|
||||
}
|
||||
|
||||
/* Handle any data written past the end of the pubk region. */
|
||||
if (offset + size > BctEndOffset) {
|
||||
const u64 diff = BctEndOffset - offset;
|
||||
R_TRY(Base::Write(BctEndOffset, buffer + diff, size - diff));
|
||||
size = diff;
|
||||
}
|
||||
|
||||
/* Read in the current BCT region. */
|
||||
R_TRY(Base::Read(0, g_boot0_bct_buffer, BctEndOffset));
|
||||
|
||||
/* Update the bct buffer. */
|
||||
for (u64 cur_offset = offset; cur_offset < BctEndOffset && cur_offset < offset + size; cur_offset++) {
|
||||
const u64 cur_bct_relative_ofs = cur_offset % BctSize;
|
||||
if (cur_bct_relative_ofs < BctPubkStart || BctPubkEnd <= cur_bct_relative_ofs) {
|
||||
g_boot0_bct_buffer[cur_offset] = buffer[cur_offset - offset];
|
||||
}
|
||||
}
|
||||
|
||||
return Base::Write(0, g_boot0_bct_buffer, BctEndOffset);
|
||||
}
|
||||
|
||||
}
|
||||
150
stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.hpp
Normal file
150
stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.hpp
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
template<class Base, size_t SectorSize>
|
||||
class SectoredStorageAdapter : public Base {
|
||||
static_assert(std::is_base_of<ams::fs::IStorage, Base>::value);
|
||||
private:
|
||||
u8 sector_buf[SectorSize];
|
||||
public:
|
||||
/* Inherit constructors. */
|
||||
using Base::Base;
|
||||
public:
|
||||
virtual Result Read(s64 offset, void *_buffer, size_t size) override {
|
||||
u8 *buffer = static_cast<u8 *>(_buffer);
|
||||
|
||||
const s64 seek = util::AlignDown(offset, SectorSize);
|
||||
const s64 sector_ofs = offset - seek;
|
||||
|
||||
/* Check if we have nothing to do. */
|
||||
if (size == 0) {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
/* Fast case. */
|
||||
if (sector_ofs == 0 && util::IsAligned(size, SectorSize)) {
|
||||
return Base::Read(offset, buffer, size);
|
||||
}
|
||||
|
||||
R_TRY(Base::Read(seek, this->sector_buf, SectorSize));
|
||||
|
||||
if (size + sector_ofs <= SectorSize) {
|
||||
/* Staying within the sector. */
|
||||
std::memcpy(buffer, this->sector_buf + sector_ofs, size);
|
||||
} else {
|
||||
/* Leaving the sector. */
|
||||
const size_t size_in_sector = SectorSize - sector_ofs;
|
||||
std::memcpy(buffer, this->sector_buf + sector_ofs, size_in_sector);
|
||||
size -= size_in_sector;
|
||||
|
||||
/* Read as many guaranteed aligned sectors as we can. */
|
||||
const size_t aligned_remaining_size = util::AlignDown(size, SectorSize);
|
||||
if (aligned_remaining_size) {
|
||||
R_TRY(Base::Read(offset + size_in_sector, buffer + size_in_sector, aligned_remaining_size));
|
||||
size -= aligned_remaining_size;
|
||||
}
|
||||
|
||||
/* Read any leftover data. */
|
||||
if (size) {
|
||||
R_TRY(Base::Read(offset + size_in_sector + aligned_remaining_size, this->sector_buf, SectorSize));
|
||||
std::memcpy(buffer + size_in_sector + aligned_remaining_size, this->sector_buf, size);
|
||||
}
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *_buffer, size_t size) override {
|
||||
const u8 *buffer = static_cast<const u8 *>(_buffer);
|
||||
|
||||
const s64 seek = util::AlignDown(offset, SectorSize);
|
||||
const s64 sector_ofs = offset - seek;
|
||||
|
||||
/* Check if we have nothing to do. */
|
||||
if (size == 0) {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
/* Fast case. */
|
||||
if (sector_ofs == 0 && util::IsAligned(size, SectorSize)) {
|
||||
return Base::Write(offset, buffer, size);
|
||||
}
|
||||
|
||||
/* Load existing sector data. */
|
||||
R_TRY(Base::Read(seek, this->sector_buf, SectorSize));
|
||||
|
||||
if (size + sector_ofs <= SectorSize) {
|
||||
/* Staying within the sector. */
|
||||
std::memcpy(this->sector_buf + sector_ofs, buffer, size);
|
||||
R_TRY(Base::Write(seek, this->sector_buf, SectorSize));
|
||||
} else {
|
||||
/* Leaving the sector. */
|
||||
const size_t size_in_sector = SectorSize - sector_ofs;
|
||||
std::memcpy(this->sector_buf + sector_ofs, buffer, size_in_sector);
|
||||
R_TRY(Base::Write(seek, this->sector_buf, SectorSize));
|
||||
size -= size_in_sector;
|
||||
|
||||
/* Write as many guaranteed aligned sectors as we can. */
|
||||
const size_t aligned_remaining_size = util::AlignDown(size, SectorSize);
|
||||
if (aligned_remaining_size) {
|
||||
R_TRY(Base::Write(offset + size_in_sector, buffer + size_in_sector, aligned_remaining_size));
|
||||
size -= aligned_remaining_size;
|
||||
}
|
||||
|
||||
/* Write any leftover data. */
|
||||
if (size) {
|
||||
R_TRY(Base::Read(offset + size_in_sector + aligned_remaining_size, this->sector_buf, SectorSize));
|
||||
std::memcpy(this->sector_buf, buffer + size_in_sector + aligned_remaining_size, size);
|
||||
R_TRY(Base::Write(offset + size_in_sector + aligned_remaining_size, this->sector_buf, SectorSize));
|
||||
}
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
};
|
||||
|
||||
/* Represents an RCM-preserving BOOT0 partition. */
|
||||
class Boot0Storage : public SectoredStorageAdapter<ams::fs::RemoteStorage, 0x200> {
|
||||
public:
|
||||
using Base = SectoredStorageAdapter<ams::fs::RemoteStorage, 0x200>;
|
||||
|
||||
static constexpr s64 BctEndOffset = 0xFC000;
|
||||
static constexpr s64 BctSize = static_cast<s64>(ams::updater::BctSize);
|
||||
static constexpr s64 BctPubkStart = 0x210;
|
||||
static constexpr s64 BctPubkSize = 0x100;
|
||||
static constexpr s64 BctPubkEnd = BctPubkStart + BctPubkSize;
|
||||
|
||||
static constexpr s64 EksStart = 0x180000;
|
||||
static constexpr s64 EksSize = static_cast<s64>(ams::updater::EksSize);
|
||||
static constexpr s64 EksEnd = EksStart + EksSize;
|
||||
private:
|
||||
sm::MitmProcessInfo client_info;
|
||||
private:
|
||||
bool CanModifyBctPublicKey();
|
||||
public:
|
||||
Boot0Storage(FsStorage *s, const sm::MitmProcessInfo &c) : Base(s), client_info(c) { /* ... */ }
|
||||
Boot0Storage(FsStorage s, const sm::MitmProcessInfo &c) : Base(s), client_info(c) { /* ... */ }
|
||||
public:
|
||||
virtual Result Read(s64 offset, void *_buffer, size_t size) override;
|
||||
virtual Result Write(s64 offset, const void *_buffer, size_t size) override;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "fsmitm_istorage_interface.hpp"
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
using namespace ams::fs;
|
||||
|
||||
Result IStorageInterface::Read(s64 offset, const sf::OutNonSecureBuffer &buffer, s64 size) {
|
||||
/* TODO: N retries on ResultDataCorrupted, we may want to eventually. */
|
||||
R_UNLESS(offset >= 0, fs::ResultInvalidOffset());
|
||||
R_UNLESS(size >= 0, fs::ResultInvalidSize());
|
||||
return this->base_storage->Read(offset, buffer.GetPointer(), size);
|
||||
}
|
||||
|
||||
Result IStorageInterface::Write(s64 offset, const sf::InNonSecureBuffer &buffer, s64 size) {
|
||||
/* TODO: N increases thread priority temporarily when writing. We may want to eventually. */
|
||||
R_UNLESS(offset >= 0, fs::ResultInvalidOffset());
|
||||
R_UNLESS(size >= 0, fs::ResultInvalidSize());
|
||||
return this->base_storage->Write(offset, buffer.GetPointer(), size);
|
||||
}
|
||||
|
||||
Result IStorageInterface::Flush() {
|
||||
return this->base_storage->Flush();
|
||||
}
|
||||
|
||||
Result IStorageInterface::SetSize(s64 size) {
|
||||
R_UNLESS(size >= 0, fs::ResultInvalidSize());
|
||||
return this->base_storage->SetSize(size);
|
||||
}
|
||||
|
||||
Result IStorageInterface::GetSize(sf::Out<s64> out) {
|
||||
return this->base_storage->GetSize(out.GetPointer());
|
||||
}
|
||||
|
||||
Result IStorageInterface::OperateRange(sf::Out<StorageQueryRangeInfo> out, s32 op_id, s64 offset, s64 size) {
|
||||
/* N includes this redundant check, so we will too. */
|
||||
R_UNLESS(out.GetPointer() != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
out->Clear();
|
||||
if (op_id == static_cast<s32>(fs::OperationId::QueryRange)) {
|
||||
fs::StorageQueryRangeInfo info;
|
||||
R_TRY(this->base_storage->OperateRange(&info, sizeof(info), fs::OperationId::QueryRange, offset, size, nullptr, 0));
|
||||
out->Merge(info);
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
class IStorageInterface : public sf::IServiceObject {
|
||||
private:
|
||||
enum class CommandId {
|
||||
Read = 0,
|
||||
Write = 1,
|
||||
Flush = 2,
|
||||
SetSize = 3,
|
||||
GetSize = 4,
|
||||
OperateRange = 5,
|
||||
};
|
||||
private:
|
||||
std::unique_ptr<ams::fs::IStorage> base_storage;
|
||||
public:
|
||||
IStorageInterface(ams::fs::IStorage *s) : base_storage(s) { /* ... */ }
|
||||
IStorageInterface(std::unique_ptr<ams::fs::IStorage> s) : base_storage(std::move(s)) { /* ... */ }
|
||||
private:
|
||||
/* Command API. */
|
||||
virtual Result Read(s64 offset, const sf::OutNonSecureBuffer &buffer, s64 size) final;
|
||||
virtual Result Write(s64 offset, const sf::InNonSecureBuffer &buffer, s64 size) final;
|
||||
virtual Result Flush() final;
|
||||
virtual Result SetSize(s64 size) final;
|
||||
virtual Result GetSize(sf::Out<s64> out) final;
|
||||
virtual Result OperateRange(sf::Out<ams::fs::StorageQueryRangeInfo> out, s32 op_id, s64 offset, s64 size) final;
|
||||
public:
|
||||
DEFINE_SERVICE_DISPATCH_TABLE {
|
||||
/* 1.0.0- */
|
||||
MAKE_SERVICE_COMMAND_META(Read),
|
||||
MAKE_SERVICE_COMMAND_META(Write),
|
||||
MAKE_SERVICE_COMMAND_META(Flush),
|
||||
MAKE_SERVICE_COMMAND_META(SetSize),
|
||||
MAKE_SERVICE_COMMAND_META(GetSize),
|
||||
|
||||
/* 4.0.0- */
|
||||
MAKE_SERVICE_COMMAND_META(OperateRange, hos::Version_400),
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -322,6 +322,14 @@ namespace ams::settings::fwdbg {
|
||||
/* Set to "normal" for normal reboot, "rcm" for rcm reboot. */
|
||||
R_ASSERT(ParseSettingsItemValue("atmosphere", "power_menu_reboot_function", "str!payload"));
|
||||
|
||||
/* Enable writing to BIS partitions for HBL. */
|
||||
/* This is probably undesirable for normal usage. */
|
||||
R_ASSERT(ParseSettingsItemValue("atmosphere", "enable_hbl_bis_write", "u8!0x0"));
|
||||
|
||||
/* Enable HBL to read the CAL0 partition. */
|
||||
/* This is probably undesirable for normal usage. */
|
||||
R_ASSERT(ParseSettingsItemValue("atmosphere", "enable_hbl_cal_read", "u8!0x0"));
|
||||
|
||||
/* Controls whether dmnt cheats should be toggled on or off by */
|
||||
/* default. 1 = toggled on by default, 0 = toggled off by default. */
|
||||
R_ASSERT(ParseSettingsItemValue("atmosphere", "dmnt_cheats_enabled_by_default", "u8!0x1"));
|
||||
|
||||
Reference in New Issue
Block a user