mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 20:31:14 +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 <exosphere.hpp>
|
||||||
#include "diskio_cpp.h"
|
#include "diskio_cpp.h"
|
||||||
#include "../fusee_sd_card.hpp"
|
#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) {
|
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));
|
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) {
|
bool diskio_read_system(void *dst, size_t size, size_t sector_index, size_t sector_count) {
|
||||||
/* TODO */
|
return R_SUCCEEDED(::ams::nxboot::ReadSystem(sector_index * 0x200, dst, size));
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool diskio_write_system(size_t sector_index, size_t sector_count, const void *src, size_t size) {
|
bool diskio_write_system(size_t sector_index, size_t sector_count, const void *src, size_t size) {
|
||||||
/* TODO */
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
|
@ -24,8 +24,10 @@ namespace ams::fs {
|
||||||
constexpr size_t MaxFiles = 8;
|
constexpr size_t MaxFiles = 8;
|
||||||
|
|
||||||
constinit bool g_is_sd_mounted = false;
|
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_sd_fs = {};
|
||||||
|
alignas(0x10) constinit FATFS g_sys_fs = {};
|
||||||
|
|
||||||
alignas(0x10) constinit FIL g_files[MaxFiles] = {};
|
alignas(0x10) constinit FIL g_files[MaxFiles] = {};
|
||||||
constinit bool g_files_opened[MaxFiles] = {};
|
constinit bool g_files_opened[MaxFiles] = {};
|
||||||
|
@ -104,16 +106,40 @@ namespace ams::fs {
|
||||||
|
|
||||||
bool MountSdCard() {
|
bool MountSdCard() {
|
||||||
AMS_ASSERT(!g_is_sd_mounted);
|
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;
|
return g_is_sd_mounted;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnmountSdCard() {
|
void UnmountSdCard() {
|
||||||
AMS_ASSERT(g_is_sd_mounted);
|
AMS_ASSERT(g_is_sd_mounted);
|
||||||
f_unmount("");
|
f_unmount("sdmc:");
|
||||||
g_is_sd_mounted = false;
|
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) {
|
Result CreateFile(const char *path, s64 size) {
|
||||||
/* Create the file. */
|
/* Create the file. */
|
||||||
FIL fp;
|
FIL fp;
|
||||||
|
|
|
@ -69,13 +69,27 @@ namespace ams::fs {
|
||||||
|
|
||||||
static_assert(util::is_pod<WriteOption>::value && sizeof(WriteOption) == sizeof(u32));
|
static_assert(util::is_pod<WriteOption>::value && sizeof(WriteOption) == sizeof(u32));
|
||||||
|
|
||||||
|
enum DirectoryEntryType {
|
||||||
|
DirectoryEntryType_Directory = 0,
|
||||||
|
DirectoryEntryType_File = 1,
|
||||||
|
};
|
||||||
|
|
||||||
struct FileHandle {
|
struct FileHandle {
|
||||||
void *_handle;
|
void *_handle;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct DirectoryHandle {
|
||||||
|
void *_handle;
|
||||||
|
};
|
||||||
|
|
||||||
bool MountSdCard();
|
bool MountSdCard();
|
||||||
void UnmountSdCard();
|
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 CreateFile(const char *path, s64 size);
|
||||||
Result CreateDirectory(const char *path);
|
Result CreateDirectory(const char *path);
|
||||||
Result OpenFile(FileHandle *out_file, const char *path, int mode);
|
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);
|
void *AllocateMemory(size_t size);
|
||||||
|
|
||||||
template<typename T>
|
ALWAYS_INLINE void *AllocateAligned(size_t size, size_t align) {
|
||||||
inline T *AllocateObject() {
|
return reinterpret_cast<void *>(util::AlignUp(reinterpret_cast<uintptr_t>(AllocateMemory(size + align)), align));
|
||||||
void * const mem = AllocateMemory(sizeof(T) + alignof(T));
|
}
|
||||||
|
|
||||||
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;
|
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);
|
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) {
|
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);
|
return sdmmc::Read(dst, size, SdCardPort, sector_index, sector_count);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ namespace ams::nxboot {
|
||||||
|
|
||||||
Result InitializeSdCard();
|
Result InitializeSdCard();
|
||||||
Result CheckSdCardConnection(sdmmc::SpeedMode *out_sm, sdmmc::BusWidth *out_bw);
|
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 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);
|
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/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include <exosphere.hpp>
|
#include <exosphere.hpp>
|
||||||
#include <exosphere/secmon/secmon_emummc_context.hpp>
|
|
||||||
#include "fusee_key_derivation.hpp"
|
#include "fusee_key_derivation.hpp"
|
||||||
#include "fusee_secondary_archive.hpp"
|
#include "fusee_secondary_archive.hpp"
|
||||||
#include "fusee_setup_horizon.hpp"
|
#include "fusee_setup_horizon.hpp"
|
||||||
#include "fusee_ini.hpp"
|
#include "fusee_ini.hpp"
|
||||||
|
#include "fusee_emummc.hpp"
|
||||||
|
#include "fusee_mmc.hpp"
|
||||||
#include "fusee_fatal.hpp"
|
#include "fusee_fatal.hpp"
|
||||||
|
#include "fs/fusee_fs_api.hpp"
|
||||||
|
|
||||||
namespace ams::nxboot {
|
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() {
|
bool ConfigureEmummc() {
|
||||||
/* Set magic. */
|
/* Set magic. */
|
||||||
g_emummc_cfg.base_cfg.magic = secmon::EmummcBaseConfiguration::Magic;
|
g_emummc_cfg.base_cfg.magic = secmon::EmummcBaseConfiguration::Magic;
|
||||||
|
@ -147,13 +167,11 @@ namespace ams::nxboot {
|
||||||
if (sector > 0) {
|
if (sector > 0) {
|
||||||
g_emummc_cfg.base_cfg.type = secmon::EmummcType_Partition;
|
g_emummc_cfg.base_cfg.type = secmon::EmummcType_Partition;
|
||||||
g_emummc_cfg.partition_cfg.start_sector = sector;
|
g_emummc_cfg.partition_cfg.start_sector = sector;
|
||||||
|
} else if (path[0] != '\x00' && IsDirectoryExist(path)) {
|
||||||
/* TODO */
|
|
||||||
} else if (/* TODO: directory exists */false) {
|
|
||||||
g_emummc_cfg.base_cfg.type = secmon::EmummcType_File;
|
g_emummc_cfg.base_cfg.type = secmon::EmummcType_File;
|
||||||
|
|
||||||
/* TODO */
|
std::strncpy(g_emummc_cfg.file_cfg.path.str, path, sizeof(g_emummc_cfg.file_cfg.path.str));
|
||||||
AMS_UNUSED(path);
|
g_emummc_cfg.file_cfg.path.str[sizeof(g_emummc_cfg.file_cfg.path.str) - 1] = '\x00';
|
||||||
} else {
|
} else {
|
||||||
ShowFatalError("Invalid emummc setting!\n");
|
ShowFatalError("Invalid emummc setting!\n");
|
||||||
}
|
}
|
||||||
|
@ -174,7 +192,9 @@ namespace ams::nxboot {
|
||||||
|
|
||||||
/* Determine whether we're using emummc. */
|
/* Determine whether we're using emummc. */
|
||||||
const bool emummc_enabled = ConfigureEmummc();
|
const bool emummc_enabled = ConfigureEmummc();
|
||||||
AMS_UNUSED(emummc_enabled);
|
|
||||||
|
/* Initialize emummc. */
|
||||||
|
InitializeEmummc(emummc_enabled, g_emummc_cfg);
|
||||||
|
|
||||||
AMS_UNUSED(hw_type);
|
AMS_UNUSED(hw_type);
|
||||||
ShowFatalError("SetupAndStartHorizon not fully implemented\n");
|
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 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 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 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 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);
|
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) {
|
void GetCmacResult(volatile SecurityEngineRegisters *SE, void *dst, size_t dst_size) {
|
||||||
const int num_words = dst_size / sizeof(u32);
|
const int num_words = dst_size / sizeof(u32);
|
||||||
for (int i = 0; i < num_words; ++i) {
|
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);
|
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) {
|
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 nothing to decrypt, succeed. */
|
||||||
if (size == 0) { return; }
|
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);
|
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) {
|
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. */
|
/* Validate the iv. */
|
||||||
AMS_ABORT_UNLESS(iv_size == AesBlockSize);
|
AMS_ABORT_UNLESS(iv_size == AesBlockSize);
|
||||||
|
|
Loading…
Reference in a new issue