Add support for file/partition based emummc on emmc,

Add support for emuSD
This commit is contained in:
Christoph Baumann
2025-05-09 12:30:41 +02:00
parent 633bb63fc2
commit 886fa9cc97
13 changed files with 346 additions and 130 deletions

View File

@@ -172,8 +172,7 @@ typedef struct {
LBA_t bitbase; /* Allocation bitmap base sector */
#endif
LBA_t winsect; /* Current sector appearing in the win[] */
BYTE pad[4];
BYTE win[FF_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */
__attribute__((aligned(8))) BYTE win[FF_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */
} FATFS;
@@ -218,7 +217,7 @@ typedef struct {
DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */
#endif
#if !FF_FS_TINY
BYTE buf[FF_MAX_SS]; /* File private data read/write window */
__attribute__((aligned(8))) BYTE buf[FF_MAX_SS]; /* File private data read/write window */
#endif
} FIL;

View File

@@ -16,7 +16,7 @@
#include <exosphere.hpp>
#include "diskio_cpp.h"
#include "../fusee_sd_card.hpp"
#include "../fusee_emummc.hpp"
#include "../fusee_mmc.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,9 +27,10 @@ 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) {
return false;
return R_SUCCEEDED(::ams::nxboot::ReadMmc(dst, size, ::ams::sdmmc::MmcPartition::MmcPartition_UserData, sector_index, sector_count));
}
bool diskio_write_system(size_t sector_index, size_t sector_count, const void *src, size_t size) {
/* Don't allow writes to eMMC GPP partition */
return false;
}

View File

@@ -26,9 +26,11 @@ namespace ams::fs {
constexpr size_t MaxFiles = 8 + 64;
constexpr size_t MaxDirectories = 2;
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 DIR g_dirs[MaxDirectories] = {};
@@ -117,6 +119,18 @@ namespace ams::fs {
}
bool MountSys() {
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 UnmountSys() {
AMS_ASSERT(g_is_sys_mounted);
f_unmount("sys:");
g_is_sys_mounted = false;
}
bool MountSdCard() {
AMS_ASSERT(!g_is_sd_mounted);
g_is_sd_mounted = f_mount(std::addressof(g_sd_fs), "sdmc:", 1) == FR_OK;

View File

@@ -98,6 +98,9 @@ namespace ams::fs {
bool MountSdCard();
void UnmountSdCard();
bool MountSys();
void UnmountSys();
Result GetEntryType(DirectoryEntryType *out_entry_type, bool *out_archive, const char *path);
Result CreateFile(const char *path, s64 size);

View File

@@ -233,7 +233,7 @@ namespace ams::nxboot {
}
void InitializeEmummc(bool emummc_enabled, const secmon::EmummcConfiguration &emummc_cfg) {
void InitializeEmummc(bool emummc_enabled, const secmon::EmummcEmmcConfiguration &emummc_cfg) {
Result result;
if (emummc_enabled) {
/* Get sd card size. */
@@ -242,34 +242,65 @@ namespace ams::nxboot {
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 += 5;
/* 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());
if(emummc_cfg.base_cfg.type == secmon::EmummcEmmcType_Partition_Emmc || emummc_cfg.base_cfg.type == secmon::EmummcEmmcType_Partition_Sd) {
/* Partition based emummc */
if(emummc_cfg.base_cfg.type == secmon::EmummcEmmcType_Partition_Emmc) {
/* When emmc based, init eMMC */
if (R_FAILED((result = InitializeMmc()))) {
ShowFatalError("Failed to initialize mmc: 0x%08" PRIx32 "\n", result.GetValue());
}
}
/* Open boot1. */
g_emummc_path[len + 5] = '1';
/* Get SD or eMMC storage */
fs::IStorage &storage = emummc_cfg.base_cfg.type == secmon::EmummcEmmcType_Partition_Sd ? static_cast<fs::IStorage&>(g_sd_card_storage) : static_cast<fs::IStorage&>(g_mmc_user_storage);
/* Get total storage size */
s64 storage_size;
if (R_FAILED((result = storage.GetSize(std::addressof(storage_size))))) {
ShowFatalError("Failed to get storage size: 0x%08" PRIx32 "!\n", result.GetValue());
}
const s64 partition_start = emummc_cfg.partition_cfg.start_sector * sdmmc::SectorSize;
g_boot0_storage = AllocateObject<fs::SubStorage>(storage, partition_start, 4_MB);
g_user_storage = AllocateObject<fs::SubStorage>(storage, partition_start + 8_MB, storage_size - (partition_start + 8_MB));
} else if (emummc_cfg.base_cfg.type == secmon::EmummcEmmcType_File_Emmc || emummc_cfg.base_cfg.type == secmon::EmummcEmmcType_File_Sd) {
/* File based emummc */
if(emummc_cfg.base_cfg.type == secmon::EmummcEmmcType_File_Emmc) {
/* When emmc based, init eMMC */
if (R_FAILED((result = InitializeMmc()))) {
ShowFatalError("Failed to initialize mmc: 0x%08" PRIx32 "\n", result.GetValue());
}
if (!fs::MountSys()) {
ShowFatalError("Failed to mount mmc!\n");
}
}
/* Set drive to read from */
if (emummc_cfg.base_cfg.type == secmon::EmummcEmmcType_File_Sd) {
std::strcpy(g_emummc_path, "sdmc:");
} else {
std::strcpy(g_emummc_path, "sys:");
}
std::memcpy(g_emummc_path + std::strlen(g_emummc_path), emummc_cfg.file_cfg.path.str, sizeof(emummc_cfg.file_cfg.path.str));
std::strcat(g_emummc_path, "/eMMC");
auto len = std::strlen(g_emummc_path);
/* Open boot0 file */
fs::FileHandle boot0_file;
std::strcat(g_emummc_path, "/boot0");
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 " %s!\n", result.GetValue(), g_emummc_path);
}
/* Check if boot1 file exists */
std::strcpy(g_emummc_path + len, "/boot1");
{
fs::DirectoryEntryType entry_type;
bool is_archive;
if (R_FAILED((result = fs::GetEntryType(std::addressof(entry_type), std::addressof(is_archive), g_emummc_path)))) {
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());
}
@@ -278,16 +309,16 @@ namespace ams::nxboot {
}
}
/* Open userdata. */
std::memcpy(g_emummc_path + len, "/00", 4);
/* Open userdata */
std::strcpy(g_emummc_path + len, "/00");
fs::FileHandle user00_file;
if (R_FAILED((result = fs::OpenFile(std::addressof(user00_file), g_emummc_path, fs::OpenMode_Read)))) {
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. */
/* Create partition */
g_boot0_storage = AllocateObject<fs::FileHandleStorage>(boot0_file);
g_user_storage = AllocateObject<EmummcFileStorage>(user00_file, len + 1);
g_user_storage = AllocateObject<EmummcFileStorage>(user00_file, len + 1);
} else {
ShowFatalError("Unknown emummc type %d\n", static_cast<int>(emummc_cfg.base_cfg.type));
}
@@ -304,6 +335,7 @@ namespace ams::nxboot {
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");
}

View File

@@ -20,7 +20,7 @@
namespace ams::nxboot {
void InitializeEmummc(bool emummc_enabled, const secmon::EmummcConfiguration &emummc_cfg);
void InitializeEmummc(bool emummc_enabled, const secmon::EmummcEmmcConfiguration &emummc_cfg);
Result ReadBoot0(s64 offset, void *dst, size_t size);
Result ReadPackage2(s64 offset, void *dst, size_t size);

View File

@@ -126,27 +126,33 @@ namespace ams::nxboot {
bool ConfigureEmummc() {
/* Set magic. */
g_emummc_cfg.base_cfg.magic = secmon::EmummcBaseConfiguration::Magic;
auto &sd_cfg = g_emummc_cfg.sd_cfg;
auto &emmc_cfg = g_emummc_cfg.emmc_cfg;
/* Parse ini. */
bool enabled = false;
emmc_cfg.base_cfg.magic = secmon::EmummcEmmcBaseConfiguration::Magic;
sd_cfg.base_cfg.magic = secmon::EmummcSdBaseConfiguration::Magic;
bool emummc_driver_enabled = false;
/* Parse emummc ini. */
u32 enabled = 0;
u32 id = 0;
u32 sector = 0;
const char *path = "";
const char *n_path = "";
{
IniSectionList sections;
if (ParseIniSafe(sections, "sdmc:/emummc/emummc.ini")) {
for (const auto &section : sections) {
/* We only care about the [emummc] section. */
for (const auto &section : sections){
/* Skip non-emummc sections */
if (std::strcmp(section.name, "emummc")) {
continue;
}
/* Handle individual fields. */
for (const auto &entry : section.kv_list) {
if (std::strcmp(entry.key, "enabled") == 0) {
enabled = entry.value[0] != '0';
if(std::strcmp(entry.key, "enabled") == 0) {
enabled = ParseDecimalInteger(entry.value);
} else if (std::strcmp(entry.key, "id") == 0) {
id = ParseHexInteger(entry.value);
} else if (std::strcmp(entry.key, "sector") == 0) {
@@ -161,26 +167,88 @@ namespace ams::nxboot {
}
}
/* Set values parsed from config. */
g_emummc_cfg.base_cfg.id = id;
std::strncpy(g_emummc_cfg.emu_dir_path.str, n_path, sizeof(g_emummc_cfg.emu_dir_path.str));
g_emummc_cfg.emu_dir_path.str[sizeof(g_emummc_cfg.emu_dir_path.str) - 1] = '\x00';
/* Set parsed values to config */
constexpr const char *emummc_err_str = "Invalid emummc setting!\n";
if (enabled) {
emmc_cfg.base_cfg.id = id;
std::strncpy(emmc_cfg.emu_dir_path.str, n_path, sizeof(emmc_cfg.emu_dir_path.str));
emmc_cfg.emu_dir_path.str[sizeof(emmc_cfg.emu_dir_path.str) - 1] = '\x00';
if (enabled == 1) {
/* SD based */
emummc_driver_enabled = true;
if (sector > 0) {
g_emummc_cfg.base_cfg.type = secmon::EmummcType_Partition;
g_emummc_cfg.partition_cfg.start_sector = sector;
emmc_cfg.base_cfg.type = secmon::EmummcEmmcType::EmummcEmmcType_Partition_Sd;
emmc_cfg.partition_cfg.start_sector = sector;
} else if (path[0] != '\x00' && IsDirectoryExist(path)) {
g_emummc_cfg.base_cfg.type = secmon::EmummcType_File;
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';
emmc_cfg.base_cfg.type = secmon::EmummcEmmcType::EmummcEmmcType_File_Sd;
std::strncpy(emmc_cfg.file_cfg.path.str, path, sizeof(emmc_cfg.file_cfg.path.str));
emmc_cfg.file_cfg.path.str[sizeof(emmc_cfg.file_cfg.path.str) - 1] = '\x00';
} else {
ShowFatalError("Invalid emummc setting!\n");
ShowFatalError(emummc_err_str);
}
} else if (enabled == 4){
/* eMMC based */
emummc_driver_enabled = true;
if (sector > 0) {
emmc_cfg.base_cfg.type = secmon::EmummcEmmcType::EmummcEmmcType_Partition_Emmc;
emmc_cfg.partition_cfg.start_sector = sector;
} else if (path[0] != '\x00' /* && IsDirectoryExist(path) */) {
/* TODO: Should check if directory exist on *eMMC* fat partition instead of SD */
emmc_cfg.base_cfg.type = secmon::EmummcEmmcType::EmummcEmmcType_File_Emmc;
std::strncpy(emmc_cfg.file_cfg.path.str, path, sizeof(emmc_cfg.file_cfg.path.str));
emmc_cfg.file_cfg.path.str[sizeof(emmc_cfg.file_cfg.path.str) - 1] = '\x00';
} else {
ShowFatalError(emummc_err_str);
}
} else if (enabled == 0) {
emmc_cfg.base_cfg.type = secmon::EmummcEmmcType::EmummcEmmcType_None;
} else {
ShowFatalError(emummc_err_str);
}
/* Parse emusd ini. */
constexpr const char *emusd_err_str = "Invalid emusd setting!\n";
u32 sd_enabled = 0;
u32 sd_sector = 0;
{
IniSectionList sections;
if (ParseIniSafe(sections, "sdmc:/emusd/emusd.ini")) {
for (const auto &section : sections){
/* Skip non-emummc sections */
if (std::strcmp(section.name, "emusd")) {
continue;
}
for (const auto &entry : section.kv_list) {
if(std::strcmp(entry.key, "enabled") == 0) {
sd_enabled = ParseDecimalInteger(entry.value);
} else if (std::strcmp(entry.key, "sector") == 0) {
sd_sector = ParseHexInteger(entry.value);
}
}
}
}
}
return enabled;
/* Set parsed values to config */
if (sd_enabled == 4) {
emummc_driver_enabled = true;
if (sd_sector > 0) {
sd_cfg.partition_cfg.start_sector = sd_sector;
sd_cfg.base_cfg.type = secmon::EmummcSdType::EmummcSdType_Partition_Emmc;
} else {
ShowFatalError(emusd_err_str);
}
} else if (sd_enabled == 0) {
sd_cfg.base_cfg.type = secmon::EmummcSdType::EmummcSdType_None;
} else {
ShowFatalError(emusd_err_str);
}
return emummc_driver_enabled;
}
u8 *LoadPackage1(fuse::SocType soc_type) {
@@ -502,7 +570,8 @@ namespace ams::nxboot {
storage_ctx.log_baud_rate = 115200;
/* Set the fs version. */
storage_ctx.emummc_cfg.base_cfg.fs_version = fs_version;
storage_ctx.emummc_cfg.emmc_cfg.base_cfg.fs_version = fs_version;
storage_ctx.emummc_cfg.sd_cfg.base_cfg.fs_version = fs_version;
/* Parse fields from exosphere.ini */
{
@@ -730,11 +799,12 @@ namespace ams::nxboot {
DeriveAllKeys(soc_type);
/* Determine whether we're using emummc. */
const bool emummc_enabled = ConfigureEmummc();
const bool emummc_driver_enabled = ConfigureEmummc();
const bool emummc_enabled = g_emummc_cfg.emmc_cfg.base_cfg.type != secmon::EmummcEmmcType_None;
/* Initialize emummc. */
/* NOTE: SYSTEM:/ accessible past this point. */
InitializeEmummc(emummc_enabled, g_emummc_cfg);
InitializeEmummc(emummc_enabled, g_emummc_cfg.emmc_cfg);
/* Read bootloader. */
const u8 * const package1 = LoadPackage1(soc_type);
@@ -752,7 +822,7 @@ namespace ams::nxboot {
const bool nogc_enabled = IsNogcEnabled(target_firmware);
/* Decide what KIPs/patches we're loading. */
const auto fs_version = ConfigureStratosphere(package2, target_firmware, emummc_enabled, nogc_enabled);
const auto fs_version = ConfigureStratosphere(package2, target_firmware, emummc_driver_enabled, nogc_enabled);
/* Setup exosphere. */
ConfigureExosphere(soc_type, target_firmware, emummc_enabled, fs_version);
@@ -761,7 +831,7 @@ namespace ams::nxboot {
StartCpu();
/* Build modified package2. */
RebuildPackage2(target_firmware, emummc_enabled);
RebuildPackage2(target_firmware, emummc_driver_enabled);
/* Wait for confirmation that exosphere is ready. */
WaitSecureMonitorState(pkg1::SecureMonitorState_Initialized);

View File

@@ -764,7 +764,7 @@ namespace ams::nxboot {
}
u32 ConfigureStratosphere(const u8 *nn_package2, ams::TargetFirmware target_firmware, bool emummc_enabled, bool nogc_enabled) {
u32 ConfigureStratosphere(const u8 *nn_package2, ams::TargetFirmware target_firmware, bool emummc_driver_enabled, bool nogc_enabled) {
/* Load KIPs off the SD card. */
{
/* Create kip dir path. */
@@ -852,13 +852,13 @@ namespace ams::nxboot {
/* Get FS version. */
const auto fs_version = GetFsVersion(fs_meta->kip_hash);
if (fs_version >= FsVersion_Count) {
if (emummc_enabled || nogc_enabled) {
if (emummc_driver_enabled || nogc_enabled) {
ShowFatalError("Failed to identify FS!\n");
}
}
/* If emummc is enabled, we need to decompress fs .text. */
if (emummc_enabled) {
if (emummc_driver_enabled) {
fs_meta->patch_segments |= (1 << 0);
}
@@ -876,7 +876,7 @@ namespace ams::nxboot {
return static_cast<u32>(fs_version);
}
void RebuildPackage2(ams::TargetFirmware target_firmware, bool emummc_enabled) {
void RebuildPackage2(ams::TargetFirmware target_firmware, bool emummc_driver_enabled) {
/* Get the external package. */
const auto &external_package = GetExternalPackage();
@@ -909,7 +909,7 @@ namespace ams::nxboot {
/* Read emummc, if needed. */
const InitialProcessHeader *emummc;
s64 emummc_size;
if (emummc_enabled) {
if (emummc_driver_enabled) {
emummc = static_cast<const InitialProcessHeader *>(ReadFile(std::addressof(emummc_size), "sdmc:/atmosphere/emummc.kip"));
if (emummc == nullptr) {
emummc = reinterpret_cast<const InitialProcessHeader *>(external_package.kips + external_package.header.emummc_meta.offset);
@@ -958,7 +958,7 @@ namespace ams::nxboot {
/* If necessary, inject emummc. */
u32 addl_text_offset = 0;
if (dst_kip->program_id == FsProgramId && emummc_enabled) {
if (dst_kip->program_id == FsProgramId && emummc_driver_enabled) {
/* Get emummc extents. */
addl_text_offset = emummc->bss_address + emummc->bss_size;
if ((emummc->flags & 7) || !util::IsAligned(addl_text_offset, 0x1000)) {