mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 12:21:18 +00:00
fusee_cpp: implement emummc/system partition mounting
This commit is contained in:
parent
8560713a60
commit
2f7012cbc6
15 changed files with 1009 additions and 18 deletions
|
@ -16,7 +16,7 @@
|
|||
#include <exosphere.hpp>
|
||||
#include "diskio_cpp.h"
|
||||
#include "../fusee_sd_card.hpp"
|
||||
|
||||
#include "../fusee_emummc.hpp"
|
||||
|
||||
bool diskio_read_sd_card(void *dst, size_t size, size_t sector_index, size_t sector_count) {
|
||||
return R_SUCCEEDED(::ams::nxboot::ReadSdCard(dst, size, sector_index, sector_count));
|
||||
|
@ -27,11 +27,9 @@ bool diskio_write_sd_card(size_t sector_index, size_t sector_count, const void *
|
|||
}
|
||||
|
||||
bool diskio_read_system(void *dst, size_t size, size_t sector_index, size_t sector_count) {
|
||||
/* TODO */
|
||||
return false;
|
||||
return R_SUCCEEDED(::ams::nxboot::ReadSystem(sector_index * 0x200, dst, size));
|
||||
}
|
||||
|
||||
bool diskio_write_system(size_t sector_index, size_t sector_count, const void *src, size_t size) {
|
||||
/* TODO */
|
||||
return false;
|
||||
}
|
|
@ -24,8 +24,10 @@ namespace ams::fs {
|
|||
constexpr size_t MaxFiles = 8;
|
||||
|
||||
constinit bool g_is_sd_mounted = false;
|
||||
constinit bool g_is_sys_mounted = false;
|
||||
|
||||
alignas(0x10) constinit FATFS g_sd_fs = {};
|
||||
alignas(0x10) constinit FATFS g_sys_fs = {};
|
||||
|
||||
alignas(0x10) constinit FIL g_files[MaxFiles] = {};
|
||||
constinit bool g_files_opened[MaxFiles] = {};
|
||||
|
@ -104,16 +106,40 @@ namespace ams::fs {
|
|||
|
||||
bool MountSdCard() {
|
||||
AMS_ASSERT(!g_is_sd_mounted);
|
||||
g_is_sd_mounted = f_mount(std::addressof(g_sd_fs), "sdmc", 1) == FR_OK;
|
||||
g_is_sd_mounted = f_mount(std::addressof(g_sd_fs), "sdmc:", 1) == FR_OK;
|
||||
return g_is_sd_mounted;
|
||||
}
|
||||
|
||||
void UnmountSdCard() {
|
||||
AMS_ASSERT(g_is_sd_mounted);
|
||||
f_unmount("");
|
||||
f_unmount("sdmc:");
|
||||
g_is_sd_mounted = false;
|
||||
}
|
||||
|
||||
bool MountSystem() {
|
||||
AMS_ASSERT(!g_is_sys_mounted);
|
||||
g_is_sys_mounted = f_mount(std::addressof(g_sys_fs), "sys:", 1) == FR_OK;
|
||||
return g_is_sys_mounted;
|
||||
}
|
||||
|
||||
void UnmountSystem() {
|
||||
AMS_ASSERT(g_is_sys_mounted);
|
||||
f_unmount("sys:");
|
||||
g_is_sys_mounted = false;
|
||||
}
|
||||
|
||||
Result GetEntryType(DirectoryEntryType *out_entry_type, bool *out_archive, const char *path) {
|
||||
/* Get the file info. */
|
||||
FILINFO info;
|
||||
R_TRY(TranslateFatFsError(f_stat(path, std::addressof(info))));
|
||||
|
||||
/* Handle the file. */
|
||||
*out_entry_type = (info.fattrib & AM_DIR) ? DirectoryEntryType_Directory : DirectoryEntryType_File;
|
||||
*out_archive = (info.fattrib & AM_ARC);
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result CreateFile(const char *path, s64 size) {
|
||||
/* Create the file. */
|
||||
FIL fp;
|
||||
|
|
|
@ -69,13 +69,27 @@ namespace ams::fs {
|
|||
|
||||
static_assert(util::is_pod<WriteOption>::value && sizeof(WriteOption) == sizeof(u32));
|
||||
|
||||
enum DirectoryEntryType {
|
||||
DirectoryEntryType_Directory = 0,
|
||||
DirectoryEntryType_File = 1,
|
||||
};
|
||||
|
||||
struct FileHandle {
|
||||
void *_handle;
|
||||
};
|
||||
|
||||
struct DirectoryHandle {
|
||||
void *_handle;
|
||||
};
|
||||
|
||||
bool MountSdCard();
|
||||
void UnmountSdCard();
|
||||
|
||||
bool MountSystem();
|
||||
void UnmountSystem();
|
||||
|
||||
Result GetEntryType(DirectoryEntryType *out_entry_type, bool *out_archive, const char *path);
|
||||
|
||||
Result CreateFile(const char *path, s64 size);
|
||||
Result CreateDirectory(const char *path);
|
||||
Result OpenFile(FileHandle *out_file, const char *path, int mode);
|
||||
|
|
73
fusee_cpp/program/source/fs/fusee_fs_file_storage.cpp
Normal file
73
fusee_cpp/program/source/fs/fusee_fs_file_storage.cpp
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "fusee_fs_storage.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
Result FileHandleStorage::UpdateSize() {
|
||||
R_SUCCEED_IF(m_size != InvalidSize);
|
||||
return GetFileSize(std::addressof(m_size), m_handle);
|
||||
}
|
||||
|
||||
Result FileHandleStorage::Read(s64 offset, void *buffer, size_t size) {
|
||||
/* Immediately succeed if there's nothing to read. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Validate buffer. */
|
||||
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
/* Ensure our size is valid. */
|
||||
R_TRY(this->UpdateSize());
|
||||
|
||||
/* Ensure our access is valid. */
|
||||
R_UNLESS(IStorage::CheckAccessRange(offset, size, m_size), fs::ResultOutOfRange());
|
||||
|
||||
return ReadFile(m_handle, offset, buffer, size, fs::ReadOption());
|
||||
}
|
||||
|
||||
Result FileHandleStorage::Write(s64 offset, const void *buffer, size_t size) {
|
||||
/* Immediately succeed if there's nothing to write. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Validate buffer. */
|
||||
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
|
||||
|
||||
/* Ensure our size is valid. */
|
||||
R_TRY(this->UpdateSize());
|
||||
|
||||
/* Ensure our access is valid. */
|
||||
R_UNLESS(IStorage::CheckAccessRange(offset, size, m_size), fs::ResultOutOfRange());
|
||||
|
||||
return WriteFile(m_handle, offset, buffer, size, fs::WriteOption());
|
||||
}
|
||||
|
||||
Result FileHandleStorage::Flush() {
|
||||
return FlushFile(m_handle);
|
||||
}
|
||||
|
||||
Result FileHandleStorage::GetSize(s64 *out_size) {
|
||||
R_TRY(this->UpdateSize());
|
||||
*out_size = m_size;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result FileHandleStorage::SetSize(s64 size) {
|
||||
m_size = InvalidSize;
|
||||
return SetFileSize(m_handle, size);
|
||||
}
|
||||
|
||||
}
|
145
fusee_cpp/program/source/fs/fusee_fs_storage.hpp
Normal file
145
fusee_cpp/program/source/fs/fusee_fs_storage.hpp
Normal file
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <exosphere.hpp>
|
||||
#include "fusee_fs_api.hpp"
|
||||
|
||||
namespace ams::fs {
|
||||
|
||||
class IStorage {
|
||||
public:
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) = 0;
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) = 0;
|
||||
|
||||
virtual Result Flush() = 0;
|
||||
|
||||
virtual Result SetSize(s64 size) = 0;
|
||||
|
||||
virtual Result GetSize(s64 *out) = 0;
|
||||
public:
|
||||
static inline bool CheckAccessRange(s64 offset, s64 size, s64 total_size) {
|
||||
return offset >= 0 &&
|
||||
size >= 0 &&
|
||||
size <= total_size &&
|
||||
offset <= (total_size - size);
|
||||
}
|
||||
|
||||
static inline bool CheckAccessRange(s64 offset, size_t size, s64 total_size) {
|
||||
return CheckAccessRange(offset, static_cast<s64>(size), total_size);
|
||||
}
|
||||
|
||||
static inline bool CheckOffsetAndSize(s64 offset, s64 size) {
|
||||
return offset >= 0 &&
|
||||
size >= 0 &&
|
||||
offset <= (offset + size);
|
||||
}
|
||||
|
||||
static inline bool CheckOffsetAndSize(s64 offset, size_t size) {
|
||||
return CheckOffsetAndSize(offset, static_cast<s64>(size));
|
||||
}
|
||||
};
|
||||
|
||||
class ReadOnlyStorageAdapter : public IStorage {
|
||||
private:
|
||||
IStorage &m_storage;
|
||||
public:
|
||||
ReadOnlyStorageAdapter(IStorage &s) : m_storage(s) { /* ... */ }
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override {
|
||||
return m_storage.Read(offset, buffer, size);
|
||||
}
|
||||
|
||||
virtual Result Flush() override {
|
||||
return m_storage.Flush();
|
||||
}
|
||||
|
||||
virtual Result GetSize(s64 *out) override {
|
||||
return m_storage.GetSize(out);
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
|
||||
return fs::ResultUnsupportedOperation();
|
||||
}
|
||||
|
||||
virtual Result SetSize(s64 size) override {
|
||||
return fs::ResultUnsupportedOperation();
|
||||
}
|
||||
};
|
||||
|
||||
class SubStorage : public IStorage {
|
||||
private:
|
||||
IStorage &m_storage;
|
||||
s64 m_offset;
|
||||
s64 m_size;
|
||||
public:
|
||||
SubStorage(IStorage &s, s64 o, s64 sz) : m_storage(s), m_offset(o), m_size(sz) { /* ... */ }
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override {
|
||||
/* Succeed immediately on zero-sized operation. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Validate arguments and read. */
|
||||
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
|
||||
R_UNLESS(IStorage::CheckAccessRange(offset, size, m_size), fs::ResultOutOfRange());
|
||||
return m_storage.Read(m_offset + offset, buffer, size);
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override{
|
||||
/* Succeed immediately on zero-sized operation. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Validate arguments and write. */
|
||||
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
|
||||
R_UNLESS(IStorage::CheckAccessRange(offset, size, m_size), fs::ResultOutOfRange());
|
||||
return m_storage.Write(m_offset + offset, buffer, size);
|
||||
}
|
||||
|
||||
virtual Result Flush() override {
|
||||
return m_storage.Flush();
|
||||
}
|
||||
|
||||
virtual Result GetSize(s64 *out) override {
|
||||
*out = m_size;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
virtual Result SetSize(s64 size) override {
|
||||
return fs::ResultUnsupportedOperationInSubStorageA();
|
||||
}
|
||||
};
|
||||
|
||||
class FileHandleStorage : public IStorage {
|
||||
private:
|
||||
static constexpr s64 InvalidSize = -1;
|
||||
private:
|
||||
FileHandle m_handle;
|
||||
s64 m_size;
|
||||
public:
|
||||
constexpr explicit FileHandleStorage(FileHandle handle) : m_handle(handle), m_size(InvalidSize) { /* ... */ }
|
||||
|
||||
~FileHandleStorage() { fs::CloseFile(m_handle); }
|
||||
protected:
|
||||
Result UpdateSize();
|
||||
public:
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override;
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override;
|
||||
virtual Result Flush() override;
|
||||
virtual Result GetSize(s64 *out_size) override;
|
||||
virtual Result SetSize(s64 size) override;
|
||||
};
|
||||
|
||||
}
|
480
fusee_cpp/program/source/fusee_emummc.cpp
Normal file
480
fusee_cpp/program/source/fusee_emummc.cpp
Normal file
|
@ -0,0 +1,480 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "fusee_emummc.hpp"
|
||||
#include "fusee_mmc.hpp"
|
||||
#include "fusee_sd_card.hpp"
|
||||
#include "fusee_fatal.hpp"
|
||||
#include "fusee_malloc.hpp"
|
||||
#include "fs/fusee_fs_api.hpp"
|
||||
#include "fs/fusee_fs_storage.hpp"
|
||||
|
||||
namespace ams::nxboot {
|
||||
|
||||
namespace {
|
||||
|
||||
class SdCardStorage : public fs::IStorage {
|
||||
public:
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override {
|
||||
if (!util::IsAligned(offset, sdmmc::SectorSize) || !util::IsAligned(size, sdmmc::SectorSize)) {
|
||||
ShowFatalError("SdCard: unaligned access to %" PRIx64 ", size=%" PRIx64"\n", static_cast<u64>(offset), static_cast<u64>(size));
|
||||
}
|
||||
|
||||
return ReadSdCard(buffer, size, offset / sdmmc::SectorSize, size / sdmmc::SectorSize);
|
||||
}
|
||||
|
||||
virtual Result Flush() override {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
virtual Result GetSize(s64 *out) override {
|
||||
u32 num_sectors;
|
||||
R_TRY(GetSdCardMemoryCapacity(std::addressof(num_sectors)));
|
||||
|
||||
*out = num_sectors * sdmmc::SectorSize;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
|
||||
return fs::ResultUnsupportedOperation();
|
||||
}
|
||||
|
||||
virtual Result SetSize(s64 size) override {
|
||||
return fs::ResultUnsupportedOperation();
|
||||
}
|
||||
};
|
||||
|
||||
template<sdmmc::MmcPartition Partition>
|
||||
class MmcPartitionStorage : public fs::IStorage {
|
||||
public:
|
||||
constexpr MmcPartitionStorage() { /* ... */ }
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override {
|
||||
if (!util::IsAligned(offset, sdmmc::SectorSize) || !util::IsAligned(size, sdmmc::SectorSize)) {
|
||||
ShowFatalError("SdCard: unaligned access to %" PRIx64 ", size=%" PRIx64"\n", static_cast<u64>(offset), static_cast<u64>(size));
|
||||
}
|
||||
|
||||
return ReadMmc(buffer, size, Partition, offset / sdmmc::SectorSize, size / sdmmc::SectorSize);
|
||||
}
|
||||
|
||||
virtual Result Flush() override {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
virtual Result GetSize(s64 *out) override {
|
||||
u32 num_sectors;
|
||||
R_TRY(GetMmcMemoryCapacity(std::addressof(num_sectors), Partition));
|
||||
|
||||
*out = num_sectors * sdmmc::SectorSize;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
|
||||
return fs::ResultUnsupportedOperation();
|
||||
}
|
||||
|
||||
virtual Result SetSize(s64 size) override {
|
||||
return fs::ResultUnsupportedOperation();
|
||||
}
|
||||
};
|
||||
|
||||
using MmcBoot0Storage = MmcPartitionStorage<sdmmc::MmcPartition_BootPartition1>;
|
||||
using MmcUserStorage = MmcPartitionStorage<sdmmc::MmcPartition_UserData>;
|
||||
|
||||
constinit char g_emummc_path[0x300];
|
||||
|
||||
class EmummcFileStorage : public fs::IStorage {
|
||||
private:
|
||||
s64 m_file_size;
|
||||
fs::FileHandle m_handle;
|
||||
int m_id;
|
||||
int m_file_path_ofs;
|
||||
private:
|
||||
void SwitchFile(int id) {
|
||||
if (m_id != id) {
|
||||
fs::CloseFile(m_handle);
|
||||
|
||||
/* Update path. */
|
||||
g_emummc_path[m_file_path_ofs + 1] = '0' + (id % 10);
|
||||
g_emummc_path[m_file_path_ofs + 0] = '0' + (id / 10);
|
||||
|
||||
/* Open new file. */
|
||||
const Result result = fs::OpenFile(std::addressof(m_handle), g_emummc_path, fs::OpenMode_Read);
|
||||
if (R_FAILED(result)) {
|
||||
ShowFatalError("Failed to open emummc user %02d file: 0x%08" PRIx32 "!\n", id, result.GetValue());
|
||||
}
|
||||
|
||||
m_id = id;
|
||||
}
|
||||
}
|
||||
public:
|
||||
EmummcFileStorage(fs::FileHandle user00, int ofs) : m_handle(user00), m_id(0), m_file_path_ofs(ofs) {
|
||||
const Result result = fs::GetFileSize(std::addressof(m_file_size), m_handle);
|
||||
if (R_FAILED(result)) {
|
||||
ShowFatalError("Failed to get emummc file size: 0x%08" PRIx32 "!\n", result.GetValue());
|
||||
}
|
||||
}
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override {
|
||||
int file = offset / m_file_size;
|
||||
s64 subofs = offset % m_file_size;
|
||||
|
||||
u8 *cur_dst = static_cast<u8 *>(buffer);
|
||||
|
||||
for (/* ... */; size > 0; ++file) {
|
||||
/* Switch to the current file. */
|
||||
SwitchFile(file);
|
||||
|
||||
/* Perform the current read. */
|
||||
const size_t cur_size = std::min<size_t>(m_file_size - subofs, size);
|
||||
R_TRY(fs::ReadFile(m_handle, subofs, cur_dst, cur_size));
|
||||
|
||||
/* Advance. */
|
||||
cur_dst += cur_size;
|
||||
size -= cur_size;
|
||||
subofs = 0;
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
virtual Result Flush() override {
|
||||
return fs::FlushFile(m_handle);
|
||||
}
|
||||
|
||||
virtual Result GetSize(s64 *out) override {
|
||||
return fs::ResultUnsupportedOperation();
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
|
||||
return fs::ResultUnsupportedOperation();
|
||||
}
|
||||
|
||||
virtual Result SetSize(s64 size) override {
|
||||
return fs::ResultUnsupportedOperation();
|
||||
}
|
||||
};
|
||||
|
||||
constinit SdCardStorage g_sd_card_storage;
|
||||
|
||||
constinit MmcBoot0Storage g_mmc_boot0_storage;
|
||||
constinit MmcUserStorage g_mmc_user_storage;
|
||||
|
||||
constinit fs::IStorage *g_boot0_storage = nullptr;
|
||||
constinit fs::IStorage *g_user_storage = nullptr;
|
||||
|
||||
class SystemPartitionStorage : public fs::IStorage {
|
||||
private:
|
||||
static constexpr size_t CacheEntries = BITSIZEOF(u32);
|
||||
static constexpr size_t SectorSize = 0x4000;
|
||||
private:
|
||||
fs::SubStorage m_storage;
|
||||
u8 *m_sector_cache;
|
||||
u32 *m_sector_ids;
|
||||
u32 m_sector_flags;
|
||||
u32 m_next_idx;
|
||||
private:
|
||||
Result LoadSector(u8 *sector, u32 sector_id) {
|
||||
/* Read the sector data. */
|
||||
R_TRY(m_storage.Read(static_cast<s64>(sector_id) * SectorSize, sector, SectorSize));
|
||||
|
||||
/* Decrypt the sector. */
|
||||
se::DecryptAes128Xts(sector, SectorSize, pkg1::AesKeySlot_BootloaderSystem0, pkg1::AesKeySlot_BootloaderSystem1, sector, SectorSize, sector_id);
|
||||
|
||||
/* Mark the sector as freshly loaded. */
|
||||
m_sector_flags &= ~(1u << (sector_id % CacheEntries));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GetSector(u8 **out_sector, u32 sector_id) {
|
||||
/* Try to find in the cache. */
|
||||
for (size_t i = 0; i < CacheEntries; ++i) {
|
||||
if (m_sector_ids[i] == sector_id) {
|
||||
m_sector_flags &= ~(1u << i);
|
||||
*out_sector = m_sector_cache + SectorSize * i;
|
||||
return ResultSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
/* Find a sector to evict. */
|
||||
while ((m_sector_flags & (1u << m_next_idx)) == 0) {
|
||||
m_sector_flags |= (1u << m_next_idx);
|
||||
m_next_idx = (m_next_idx + 1) % CacheEntries;
|
||||
}
|
||||
|
||||
/* Get the chosen sector. */
|
||||
*out_sector = m_sector_cache + SectorSize * m_next_idx;
|
||||
m_next_idx = (m_next_idx + 1) % CacheEntries;
|
||||
|
||||
/* Load the sector. */
|
||||
return this->LoadSector(*out_sector, sector_id);
|
||||
}
|
||||
public:
|
||||
SystemPartitionStorage(s64 ofs, s64 size) : m_storage(*g_user_storage, ofs, size) {
|
||||
/* Allocate sector cache. */
|
||||
m_sector_cache = static_cast<u8 *>(AllocateAligned(CacheEntries * SectorSize, SectorSize));
|
||||
|
||||
/* Allocate sector ids. */
|
||||
m_sector_ids = static_cast<u32 *>(AllocateAligned(CacheEntries * sizeof(u32), alignof(u32)));
|
||||
for (size_t i = 0; i < CacheEntries; ++i) {
|
||||
m_sector_ids[i] = std::numeric_limits<u32>::max();
|
||||
}
|
||||
|
||||
/* All sectors are dirty. */
|
||||
m_sector_flags = ~0u;
|
||||
|
||||
/* Next sector is 0. */
|
||||
m_next_idx = 0;
|
||||
}
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override {
|
||||
u32 sector_id = offset / SectorSize;
|
||||
s64 subofs = offset % SectorSize;
|
||||
|
||||
u8 *cur_dst = static_cast<u8 *>(buffer);
|
||||
while (size > 0) {
|
||||
/* Get the current sector. */
|
||||
u8 *sector;
|
||||
R_TRY(this->GetSector(std::addressof(sector), sector_id++));
|
||||
|
||||
/* Copy the data. */
|
||||
const size_t cur_size = std::min<size_t>(SectorSize - subofs, size);
|
||||
std::memcpy(cur_dst, sector + subofs, cur_size);
|
||||
|
||||
/* Advance. */
|
||||
cur_dst += cur_size;
|
||||
size -= cur_size;
|
||||
subofs = 0;
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
virtual Result Flush() override {
|
||||
return m_storage.Flush();
|
||||
}
|
||||
|
||||
virtual Result GetSize(s64 *out) override {
|
||||
return m_storage.GetSize(out);
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
|
||||
return fs::ResultUnsupportedOperation();
|
||||
}
|
||||
|
||||
virtual Result SetSize(s64 size) override {
|
||||
return fs::ResultUnsupportedOperation();
|
||||
}
|
||||
};
|
||||
|
||||
constinit SystemPartitionStorage *g_system_storage = nullptr;
|
||||
constinit fs::SubStorage *g_package2_storage = nullptr;
|
||||
|
||||
struct Guid {
|
||||
u32 data1;
|
||||
u16 data2;
|
||||
u16 data3;
|
||||
u8 data4[8];
|
||||
};
|
||||
static_assert(sizeof(Guid) == 0x10);
|
||||
|
||||
struct GptHeader {
|
||||
char signature[8];
|
||||
u32 revision;
|
||||
u32 header_size;
|
||||
u32 header_crc32;
|
||||
u32 reserved0;
|
||||
u64 my_lba;
|
||||
u64 alt_lba;
|
||||
u64 first_usable_lba;
|
||||
u64 last_usable_lba;
|
||||
Guid disk_guid;
|
||||
u64 partition_entry_lba;
|
||||
u32 number_of_partition_entries;
|
||||
u32 size_of_partition_entry;
|
||||
u32 partition_entry_array_crc32;
|
||||
u32 reserved1;
|
||||
};
|
||||
static_assert(sizeof(GptHeader) == 0x60);
|
||||
|
||||
struct GptPartitionEntry {
|
||||
Guid partition_type_guid;
|
||||
Guid unique_partition_guid;
|
||||
u64 starting_lba;
|
||||
u64 ending_lba;
|
||||
u64 attributes;
|
||||
char partition_name[0x48];
|
||||
};
|
||||
static_assert(sizeof(GptPartitionEntry) == 0x80);
|
||||
|
||||
struct Gpt {
|
||||
GptHeader header;
|
||||
u8 padding[0x1A0];
|
||||
GptPartitionEntry entries[128];
|
||||
};
|
||||
static_assert(sizeof(Gpt) == 16_KB + 0x200);
|
||||
|
||||
constexpr const u16 SystemPartitionName[] = {
|
||||
'S', 'Y', 'S', 'T', 'E', 'M', 0
|
||||
};
|
||||
|
||||
constexpr const u16 Package2PartitionName[] = {
|
||||
'B', 'C', 'P', 'K', 'G', '2', '-', '1', '-', 'N', 'o', 'r', 'm', 'a', 'l', '-', 'M', 'a', 'i', 'n', 0
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void InitializeEmummc(bool emummc_enabled, const secmon::EmummcConfiguration &emummc_cfg) {
|
||||
Result result;
|
||||
if (emummc_enabled) {
|
||||
/* Get sd card size. */
|
||||
s64 sd_card_size;
|
||||
if (R_FAILED((result = g_sd_card_storage.GetSize(std::addressof(sd_card_size))))) {
|
||||
ShowFatalError("Failed to get sd card size: 0x%08" PRIx32 "!\n", result.GetValue());
|
||||
}
|
||||
|
||||
if (emummc_cfg.base_cfg.type == secmon::EmummcType_Partition) {
|
||||
const s64 partition_start = emummc_cfg.partition_cfg.start_sector * sdmmc::SectorSize;
|
||||
g_boot0_storage = AllocateObject<fs::SubStorage>(g_sd_card_storage, partition_start, 4_MB);
|
||||
g_user_storage = AllocateObject<fs::SubStorage>(g_sd_card_storage, partition_start + 8_MB, sd_card_size - (partition_start + 8_MB));
|
||||
} else if (emummc_cfg.base_cfg.type == secmon::EmummcType_File) {
|
||||
/* Get the base emummc path. */
|
||||
std::memcpy(g_emummc_path, emummc_cfg.file_cfg.path.str, sizeof(emummc_cfg.file_cfg.path.str));
|
||||
|
||||
/* Get path length. */
|
||||
auto len = std::strlen(g_emummc_path);
|
||||
|
||||
/* Append emmc. */
|
||||
std::memcpy(g_emummc_path + len, "/eMMC", 6);
|
||||
len += 6;
|
||||
|
||||
/* Open boot0. */
|
||||
fs::FileHandle boot0_file;
|
||||
std::memcpy(g_emummc_path + len, "/boot0", 7);
|
||||
if (R_FAILED((result = fs::OpenFile(std::addressof(boot0_file), g_emummc_path, fs::OpenMode_Read)))) {
|
||||
ShowFatalError("Failed to open emummc boot0 file: 0x%08" PRIx32 "!\n", result.GetValue());
|
||||
}
|
||||
|
||||
/* Open boot1. */
|
||||
g_emummc_path[len + 5] = '1';
|
||||
{
|
||||
fs::DirectoryEntryType entry_type;
|
||||
bool is_archive;
|
||||
if (R_FAILED((result = fs::GetEntryType(std::addressof(entry_type), std::addressof(is_archive), g_emummc_path)))) {
|
||||
ShowFatalError("Failed to find emummc boot1 file: 0x%08" PRIx32 "!\n", result.GetValue());
|
||||
}
|
||||
|
||||
if (entry_type != fs::DirectoryEntryType_File) {
|
||||
ShowFatalError("emummc boot1 file is not a file!\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Open userdata. */
|
||||
std::memcpy(g_emummc_path + len, "/00", 4);
|
||||
fs::FileHandle user00_file;
|
||||
if (R_FAILED((result = fs::OpenFile(std::addressof(user00_file), g_emummc_path, fs::OpenMode_Read)))) {
|
||||
ShowFatalError("Failed to open emummc user %02d file: 0x%08" PRIx32 "!\n", 0, result.GetValue());
|
||||
}
|
||||
|
||||
/* Create partitions. */
|
||||
g_boot0_storage = AllocateObject<fs::FileHandleStorage>(boot0_file);
|
||||
g_user_storage = AllocateObject<EmummcFileStorage>(user00_file, len + 1);
|
||||
} else {
|
||||
ShowFatalError("Unknown emummc type %d\n", static_cast<int>(emummc_cfg.base_cfg.type));
|
||||
}
|
||||
} else {
|
||||
/* Initialize access to mmc. */
|
||||
{
|
||||
const Result result = InitializeMmc();
|
||||
if (R_FAILED(result)) {
|
||||
ShowFatalError("Failed to initialize mmc: 0x%08" PRIx32 "\n", result.GetValue());
|
||||
}
|
||||
}
|
||||
|
||||
/* Create storages. */
|
||||
g_boot0_storage = std::addressof(g_mmc_boot0_storage);
|
||||
g_user_storage = std::addressof(g_mmc_user_storage);
|
||||
}
|
||||
if (g_boot0_storage == nullptr) {
|
||||
ShowFatalError("Failed to initialize BOOT0\n");
|
||||
}
|
||||
if (g_user_storage == nullptr) {
|
||||
ShowFatalError("Failed to initialize Raw EMMC\n");
|
||||
}
|
||||
|
||||
/* Read the GPT. */
|
||||
Gpt *gpt = static_cast<Gpt *>(AllocateAligned(sizeof(Gpt), 0x200));
|
||||
{
|
||||
const Result result = g_user_storage->Read(0x200, gpt, sizeof(*gpt));
|
||||
if (R_FAILED(result)) {
|
||||
ShowFatalError("Failed to read GPT: 0x%08" PRIx32 "\n", result.GetValue());
|
||||
}
|
||||
}
|
||||
|
||||
/* Check the GPT. */
|
||||
if (std::memcmp(gpt->header.signature, "EFI PART", 8) != 0) {
|
||||
ShowFatalError("Invalid GPT signature\n");
|
||||
}
|
||||
if (gpt->header.number_of_partition_entries > util::size(gpt->entries)) {
|
||||
ShowFatalError("Too many GPT entries\n");
|
||||
}
|
||||
|
||||
/* Create system storage. */
|
||||
for (u32 i = 0; i < gpt->header.number_of_partition_entries; ++i) {
|
||||
if (gpt->entries[i].starting_lba < gpt->header.first_usable_lba) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const s64 offset = 0x200 * gpt->entries[i].starting_lba;
|
||||
const u64 size = 0x200 * (gpt->entries[i].ending_lba + 1 - gpt->entries[i].starting_lba);
|
||||
|
||||
if (std::memcmp(gpt->entries[i].partition_name, SystemPartitionName, sizeof(SystemPartitionName)) == 0) {
|
||||
g_system_storage = AllocateObject<SystemPartitionStorage>(offset, size);
|
||||
} else if (std::memcmp(gpt->entries[i].partition_name, Package2PartitionName, sizeof(Package2PartitionName)) == 0) {
|
||||
g_package2_storage = AllocateObject<fs::SubStorage>(*g_user_storage, offset, size);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check that we created system storage. */
|
||||
if (g_system_storage == nullptr) {
|
||||
ShowFatalError("Failed to initialize SYSTEM\n");
|
||||
}
|
||||
|
||||
/* Check that we created package2 storage. */
|
||||
if (g_package2_storage == nullptr) {
|
||||
ShowFatalError("Failed to initialize Package2\n");
|
||||
}
|
||||
|
||||
/* Mount system. */
|
||||
if (!fs::MountSystem()) {
|
||||
ShowFatalError("Failed to mount SYSTEM\n");
|
||||
}
|
||||
}
|
||||
|
||||
Result ReadBoot0(s64 offset, void *dst, size_t size) {
|
||||
return g_boot0_storage->Read(offset, dst, size);
|
||||
}
|
||||
|
||||
Result ReadPackage2(s64 offset, void *dst, size_t size) {
|
||||
return g_package2_storage->Read(offset, dst, size);
|
||||
}
|
||||
|
||||
Result ReadSystem(s64 offset, void *dst, size_t size) {
|
||||
return g_system_storage->Read(offset, dst, size);
|
||||
}
|
||||
|
||||
}
|
29
fusee_cpp/program/source/fusee_emummc.hpp
Normal file
29
fusee_cpp/program/source/fusee_emummc.hpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <vapours.hpp>
|
||||
#include <exosphere/secmon/secmon_emummc_context.hpp>
|
||||
|
||||
namespace ams::nxboot {
|
||||
|
||||
void InitializeEmummc(bool emummc_enabled, const secmon::EmummcConfiguration &emummc_cfg);
|
||||
|
||||
Result ReadBoot0(s64 offset, void *dst, size_t size);
|
||||
Result ReadPackage2(s64 offset, void *dst, size_t size);
|
||||
Result ReadSystem(s64 offset, void *dst, size_t size);
|
||||
|
||||
}
|
|
@ -20,13 +20,15 @@ namespace ams::nxboot {
|
|||
|
||||
void *AllocateMemory(size_t size);
|
||||
|
||||
template<typename T>
|
||||
inline T *AllocateObject() {
|
||||
void * const mem = AllocateMemory(sizeof(T) + alignof(T));
|
||||
ALWAYS_INLINE void *AllocateAligned(size_t size, size_t align) {
|
||||
return reinterpret_cast<void *>(util::AlignUp(reinterpret_cast<uintptr_t>(AllocateMemory(size + align)), align));
|
||||
}
|
||||
|
||||
T * const obj = reinterpret_cast<T *>(util::AlignUp(reinterpret_cast<uintptr_t>(mem), alignof(T)));
|
||||
template<typename T, typename... Args> requires std::constructible_from<T, Args...>
|
||||
inline T *AllocateObject(Args &&... args) {
|
||||
T * const obj = static_cast<T *>(AllocateAligned(sizeof(T), alignof(T)));
|
||||
|
||||
std::construct_at(obj);
|
||||
std::construct_at(obj, std::forward<Args>(args)...);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
|
75
fusee_cpp/program/source/fusee_mmc.cpp
Normal file
75
fusee_cpp/program/source/fusee_mmc.cpp
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "fusee_mmc.hpp"
|
||||
|
||||
namespace ams::nxboot {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline auto MmcPort = sdmmc::Port_Mmc0;
|
||||
|
||||
alignas(0x10) constinit u8 g_mmc_work_buffer[sdmmc::MmcWorkBufferSize];
|
||||
|
||||
constinit inline auto g_mmc_partition = sdmmc::MmcPartition_Unknown;
|
||||
|
||||
Result SelectMmcPartition(sdmmc::MmcPartition partition) {
|
||||
/* Change partition, if we need to. */
|
||||
if (partition != g_mmc_partition) {
|
||||
R_TRY(sdmmc::SelectMmcPartition(MmcPort, partition));
|
||||
|
||||
g_mmc_partition = partition;
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result InitializeMmc() {
|
||||
/* Initialize the mmc. */
|
||||
sdmmc::Initialize(MmcPort);
|
||||
|
||||
/* Set the mmc work buffer. */
|
||||
sdmmc::SetMmcWorkBuffer(MmcPort, g_mmc_work_buffer, sizeof(g_mmc_work_buffer));
|
||||
|
||||
/* Activate the mmc. */
|
||||
return sdmmc::Activate(MmcPort);
|
||||
}
|
||||
|
||||
Result CheckMmcConnection(sdmmc::SpeedMode *out_sm, sdmmc::BusWidth *out_bw) {
|
||||
return sdmmc::CheckMmcConnection(out_sm, out_bw, MmcPort);
|
||||
}
|
||||
|
||||
Result GetMmcMemoryCapacity(u32 *out_num_sectors, sdmmc::MmcPartition partition) {
|
||||
if (partition == sdmmc::MmcPartition_UserData) {
|
||||
return sdmmc::GetDeviceMemoryCapacity(out_num_sectors, MmcPort);
|
||||
} else {
|
||||
return sdmmc::GetMmcBootPartitionCapacity(out_num_sectors, MmcPort);
|
||||
}
|
||||
}
|
||||
|
||||
Result ReadMmc(void *dst, size_t size, sdmmc::MmcPartition partition, size_t sector_index, size_t sector_count) {
|
||||
R_TRY(SelectMmcPartition(partition));
|
||||
return sdmmc::Read(dst, size, MmcPort, sector_index, sector_count);
|
||||
}
|
||||
|
||||
Result WriteMmc(sdmmc::MmcPartition partition, size_t sector_index, size_t sector_count, const void *src, size_t size) {
|
||||
R_TRY(SelectMmcPartition(partition));
|
||||
return sdmmc::Write(MmcPort, sector_index, sector_count, src, size);
|
||||
}
|
||||
|
||||
}
|
28
fusee_cpp/program/source/fusee_mmc.hpp
Normal file
28
fusee_cpp/program/source/fusee_mmc.hpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
#pragma once
|
||||
|
||||
namespace ams::nxboot {
|
||||
|
||||
Result InitializeMmc();
|
||||
Result CheckMmcConnection(sdmmc::SpeedMode *out_sm, sdmmc::BusWidth *out_bw);
|
||||
Result GetMmcMemoryCapacity(u32 *out_num_sectors, sdmmc::MmcPartition partition);
|
||||
|
||||
Result ReadMmc(void *dst, size_t size, sdmmc::MmcPartition partition, size_t sector_index, size_t sector_count);
|
||||
Result WriteMmc(sdmmc::MmcPartition partition, size_t sector_index, size_t sector_count, const void *src, size_t size);
|
||||
|
||||
}
|
|
@ -85,6 +85,10 @@ namespace ams::nxboot {
|
|||
return sdmmc::CheckSdCardConnection(out_sm, out_bw, SdCardPort);
|
||||
}
|
||||
|
||||
Result GetSdCardMemoryCapacity(u32 *out_num_sectors) {
|
||||
return sdmmc::GetDeviceMemoryCapacity(out_num_sectors, SdCardPort);
|
||||
}
|
||||
|
||||
Result ReadSdCard(void *dst, size_t size, size_t sector_index, size_t sector_count) {
|
||||
return sdmmc::Read(dst, size, SdCardPort, sector_index, sector_count);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ namespace ams::nxboot {
|
|||
|
||||
Result InitializeSdCard();
|
||||
Result CheckSdCardConnection(sdmmc::SpeedMode *out_sm, sdmmc::BusWidth *out_bw);
|
||||
Result GetSdCardMemoryCapacity(u32 *out_num_sectors);
|
||||
|
||||
Result ReadSdCard(void *dst, size_t size, size_t sector_index, size_t sector_count);
|
||||
Result WriteSdCard(size_t sector_index, size_t sector_count, const void *src, size_t size);
|
||||
|
||||
|
|
|
@ -14,12 +14,14 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include <exosphere/secmon/secmon_emummc_context.hpp>
|
||||
#include "fusee_key_derivation.hpp"
|
||||
#include "fusee_secondary_archive.hpp"
|
||||
#include "fusee_setup_horizon.hpp"
|
||||
#include "fusee_ini.hpp"
|
||||
#include "fusee_emummc.hpp"
|
||||
#include "fusee_mmc.hpp"
|
||||
#include "fusee_fatal.hpp"
|
||||
#include "fs/fusee_fs_api.hpp"
|
||||
|
||||
namespace ams::nxboot {
|
||||
|
||||
|
@ -101,6 +103,24 @@ namespace ams::nxboot {
|
|||
}
|
||||
}
|
||||
|
||||
bool IsDirectoryExist(const char *path) {
|
||||
fs::DirectoryEntryType entry_type;
|
||||
bool archive;
|
||||
return R_SUCCEEDED(fs::GetEntryType(std::addressof(entry_type), std::addressof(archive), path)) && entry_type == fs::DirectoryEntryType_Directory;
|
||||
}
|
||||
|
||||
[[maybe_unused]] bool IsFileExist(const char *path) {
|
||||
fs::DirectoryEntryType entry_type;
|
||||
bool archive;
|
||||
return R_SUCCEEDED(fs::GetEntryType(std::addressof(entry_type), std::addressof(archive), path)) && entry_type == fs::DirectoryEntryType_File;
|
||||
}
|
||||
|
||||
[[maybe_unused]] bool IsConcatenationFileExist(const char *path) {
|
||||
fs::DirectoryEntryType entry_type;
|
||||
bool archive;
|
||||
return R_SUCCEEDED(fs::GetEntryType(std::addressof(entry_type), std::addressof(archive), path)) && ((entry_type == fs::DirectoryEntryType_File) || (entry_type == fs::DirectoryEntryType_Directory && archive));
|
||||
}
|
||||
|
||||
bool ConfigureEmummc() {
|
||||
/* Set magic. */
|
||||
g_emummc_cfg.base_cfg.magic = secmon::EmummcBaseConfiguration::Magic;
|
||||
|
@ -147,13 +167,11 @@ namespace ams::nxboot {
|
|||
if (sector > 0) {
|
||||
g_emummc_cfg.base_cfg.type = secmon::EmummcType_Partition;
|
||||
g_emummc_cfg.partition_cfg.start_sector = sector;
|
||||
|
||||
/* TODO */
|
||||
} else if (/* TODO: directory exists */false) {
|
||||
} else if (path[0] != '\x00' && IsDirectoryExist(path)) {
|
||||
g_emummc_cfg.base_cfg.type = secmon::EmummcType_File;
|
||||
|
||||
/* TODO */
|
||||
AMS_UNUSED(path);
|
||||
std::strncpy(g_emummc_cfg.file_cfg.path.str, path, sizeof(g_emummc_cfg.file_cfg.path.str));
|
||||
g_emummc_cfg.file_cfg.path.str[sizeof(g_emummc_cfg.file_cfg.path.str) - 1] = '\x00';
|
||||
} else {
|
||||
ShowFatalError("Invalid emummc setting!\n");
|
||||
}
|
||||
|
@ -174,7 +192,9 @@ namespace ams::nxboot {
|
|||
|
||||
/* Determine whether we're using emummc. */
|
||||
const bool emummc_enabled = ConfigureEmummc();
|
||||
AMS_UNUSED(emummc_enabled);
|
||||
|
||||
/* Initialize emummc. */
|
||||
InitializeEmummc(emummc_enabled, g_emummc_cfg);
|
||||
|
||||
AMS_UNUSED(hw_type);
|
||||
ShowFatalError("SetupAndStartHorizon not fully implemented\n");
|
||||
|
|
|
@ -47,6 +47,8 @@ namespace ams::se {
|
|||
void DecryptAes128Cbc(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size);
|
||||
void DecryptAes256Cbc(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size);
|
||||
|
||||
void DecryptAes128Xts(void *dst, size_t dst_size, int slot_enc, int slot_tweak, const void *src, size_t src_size, size_t sector);
|
||||
|
||||
void EncryptAes128CbcAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler);
|
||||
void DecryptAes128CbcAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler);
|
||||
void ComputeAes128CtrAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler);
|
||||
|
|
|
@ -191,6 +191,21 @@ namespace ams::se {
|
|||
}
|
||||
}
|
||||
|
||||
void ExpandSubkeyLittleEndian(u8 *subkey) {
|
||||
/* Shift everything left one bit. */
|
||||
u8 prev = 0;
|
||||
for (size_t i = 0; i < AesBlockSize; ++i) {
|
||||
const u8 top = (subkey[i] >> 7);
|
||||
subkey[i] = ((subkey[i] << 1) | prev);
|
||||
prev = top;
|
||||
}
|
||||
|
||||
/* And xor with Rb if necessary. */
|
||||
if (prev != 0) {
|
||||
subkey[0] ^= 0x87;
|
||||
}
|
||||
}
|
||||
|
||||
void GetCmacResult(volatile SecurityEngineRegisters *SE, void *dst, size_t dst_size) {
|
||||
const int num_words = dst_size / sizeof(u32);
|
||||
for (int i = 0; i < num_words; ++i) {
|
||||
|
@ -339,6 +354,80 @@ namespace ams::se {
|
|||
ExecuteOperation(SE, SE_OPERATION_OP_START, dst, dst_size, src, aligned_size);
|
||||
}
|
||||
|
||||
void XorWithXtsTweak(void *dst, size_t dst_size, const void *src, size_t src_size, const void *base_tweak) {
|
||||
/* Copy tweak. */
|
||||
u8 tweak[se::AesBlockSize];
|
||||
std::memcpy(tweak, base_tweak, sizeof(tweak));
|
||||
|
||||
/* Perform xor. */
|
||||
u8 *dst_u8 = static_cast<u8 *>(dst);
|
||||
const u8 *src_u8 = static_cast<const u8 *>(src);
|
||||
|
||||
const size_t num_blocks = std::min<size_t>(dst_size, src_size) / sizeof(tweak);
|
||||
for (size_t i = 0; i < num_blocks; ++i) {
|
||||
for (size_t j = 0; j < sizeof(tweak); ++j) {
|
||||
dst_u8[j] = src_u8[j] ^ tweak[j];
|
||||
}
|
||||
|
||||
dst_u8 += sizeof(tweak);
|
||||
src_u8 += sizeof(tweak);
|
||||
|
||||
ExpandSubkeyLittleEndian(tweak);
|
||||
}
|
||||
}
|
||||
|
||||
void DecryptAesXts(void *dst, size_t dst_size, int slot_enc, int slot_tweak, const void *src, size_t src_size, size_t sector, AesMode mode) {
|
||||
/* If nothing to decrypt, succeed. */
|
||||
if (src_size == 0) { return; }
|
||||
|
||||
/* Validate input. */
|
||||
AMS_ABORT_UNLESS(util::IsAligned(dst_size, AesBlockSize));
|
||||
AMS_ABORT_UNLESS(util::IsAligned(src_size, AesBlockSize));
|
||||
AMS_ABORT_UNLESS(0 <= slot_enc && slot_enc < AesKeySlotCount);
|
||||
AMS_ABORT_UNLESS(0 <= slot_tweak && slot_tweak < AesKeySlotCount);
|
||||
|
||||
/* Generate tweak. */
|
||||
u32 base_tweak[se::AesBlockSize / sizeof(u32)] = {};
|
||||
base_tweak[util::size(base_tweak) - 1] = util::ConvertToBigEndian<u32>(static_cast<u32>(sector));
|
||||
if constexpr (sizeof(sector) > sizeof(u32)) {
|
||||
static_assert(sizeof(sector) <= sizeof(u64));
|
||||
base_tweak[util::size(base_tweak) - 2] = util::ConvertToBigEndian<u32>(static_cast<u32>(sector >> BITSIZEOF(u32)));
|
||||
}
|
||||
se::EncryptAes128(base_tweak, sizeof(base_tweak), slot_tweak, base_tweak, sizeof(base_tweak));
|
||||
|
||||
/* Xor all data. */
|
||||
XorWithXtsTweak(dst, dst_size, src, src_size, base_tweak);
|
||||
|
||||
/* Ensure the SE sees correct data. */
|
||||
hw::FlushDataCache(dst, dst_size);
|
||||
|
||||
/* Decrypt all data. */
|
||||
{
|
||||
/* Get the engine. */
|
||||
auto *SE = GetRegisters();
|
||||
|
||||
/* Determine extents. */
|
||||
const size_t num_blocks = dst_size / AesBlockSize;
|
||||
|
||||
/* Configure for AES-ECB decryption to memory. */
|
||||
SetConfig(SE, false, SE_CONFIG_DST_MEMORY);
|
||||
SetAesConfig(SE, slot_enc, false, AesConfigEcb);
|
||||
UpdateAesMode(SE, mode);
|
||||
|
||||
/* Set the block count. */
|
||||
SetBlockCount(SE, num_blocks);
|
||||
|
||||
/* Execute the operation. */
|
||||
ExecuteOperation(SE, SE_OPERATION_OP_START, dst, dst_size, dst, dst_size);
|
||||
|
||||
/* Ensure the cpu sees correct data. */
|
||||
hw::InvalidateDataCache(dst, dst_size);
|
||||
}
|
||||
|
||||
/* Xor all data. */
|
||||
XorWithXtsTweak(dst, dst_size, dst, dst_size, base_tweak);
|
||||
}
|
||||
|
||||
void ComputeAes128Async(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, DoneHandler handler, u32 config, bool encrypt, volatile SecurityEngineRegisters *SE) {
|
||||
/* If nothing to decrypt, succeed. */
|
||||
if (size == 0) { return; }
|
||||
|
@ -562,6 +651,10 @@ namespace ams::se {
|
|||
return DecryptAesCbc(dst, dst_size, slot, src, src_size, iv, iv_size, AesMode_Aes256);
|
||||
}
|
||||
|
||||
void DecryptAes128Xts(void *dst, size_t dst_size, int slot_enc, int slot_tweak, const void *src, size_t src_size, size_t sector) {
|
||||
return DecryptAesXts(dst, dst_size, slot_enc, slot_tweak, src, src_size, sector, AesMode_Aes128);
|
||||
}
|
||||
|
||||
void EncryptAes128CbcAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler) {
|
||||
/* Validate the iv. */
|
||||
AMS_ABORT_UNLESS(iv_size == AesBlockSize);
|
||||
|
|
Loading…
Reference in a new issue