From bd7f572989de35d5ae7188ba967da56ca76fe7de Mon Sep 17 00:00:00 2001 From: "ctcaer@gmail.com" Date: Sun, 30 Jun 2019 03:24:58 +0300 Subject: [PATCH] [emuMMC] Add support --- bootloader/frontend/fe_emmc_tools.c | 2 + bootloader/hos/fss.c | 6 +- bootloader/hos/hos.c | 18 +- bootloader/hos/hos.h | 1 + bootloader/hos/pkg2.c | 101 ++++++++++- bootloader/hos/secmon_exo.c | 78 +++++++- bootloader/hos/sept.c | 12 +- bootloader/main.c | 23 ++- bootloader/storage/emummc.c | 265 ++++++++++++++++++++++++++++ bootloader/storage/emummc.h | 59 +++++++ bootloader/storage/nx_emmc.c | 5 +- 11 files changed, 544 insertions(+), 26 deletions(-) create mode 100644 bootloader/storage/emummc.c create mode 100644 bootloader/storage/emummc.h diff --git a/bootloader/frontend/fe_emmc_tools.c b/bootloader/frontend/fe_emmc_tools.c index 863329d..d3e3704 100644 --- a/bootloader/frontend/fe_emmc_tools.c +++ b/bootloader/frontend/fe_emmc_tools.c @@ -995,3 +995,5 @@ out: void restore_emmc_boot() { _restore_emmc_selected(PART_BOOT); } void restore_emmc_rawnand() { _restore_emmc_selected(PART_RAW); } void restore_emmc_gpp_parts() { _restore_emmc_selected(PART_GP_ALL); } + +#pragma GCC pop_options diff --git a/bootloader/hos/fss.c b/bootloader/hos/fss.c index b6e2eeb..c42d52f 100644 --- a/bootloader/hos/fss.c +++ b/bootloader/hos/fss.c @@ -22,10 +22,13 @@ #include "hos.h" #include "../libs/fatfs/ff.h" #include "../mem/heap.h" +#include "../storage/emummc.h" #include "../gfx/gfx.h" #define DPRINTF(...) +extern hekate_config h_cfg; + #define FSS0_MAGIC 0x30535346 #define CNT_TYPE_FSP 0 #define CNT_TYPE_EXO 1 @@ -35,6 +38,7 @@ #define CNT_TYPE_SP2 5 #define CNT_TYPE_KIP 6 #define CNT_TYPE_BMP 7 +#define CNT_TYPE_EMC 8 typedef struct _fss_t { @@ -70,7 +74,7 @@ int parse_fss(launch_ctxt_t *ctxt, const char *value) stock = true; } - if (stock && ctxt->pkg1_id->kb <= KB_FIRMWARE_VERSION_620) + if (stock && ctxt->pkg1_id->kb <= KB_FIRMWARE_VERSION_620 && (!emu_cfg.enabled || h_cfg.emummc_force_disable)) return 1; if (f_open(&fp, value, FA_READ) != FR_OK) diff --git a/bootloader/hos/hos.c b/bootloader/hos/hos.c index bc376eb..2623ec1 100644 --- a/bootloader/hos/hos.c +++ b/bootloader/hos/hos.c @@ -36,6 +36,7 @@ #include "../soc/pmc.h" #include "../soc/smmu.h" #include "../soc/t210.h" +#include "../storage/emummc.h" #include "../storage/nx_emmc.h" #include "../storage/sdmmc.h" #include "../utils/util.h" @@ -44,6 +45,7 @@ extern hekate_config h_cfg; extern void sd_unmount(); +extern bool sd_mount(); //#define DPRINTF(...) gfx_printf(__VA_ARGS__) #define DPRINTF(...) @@ -296,12 +298,12 @@ static int _read_emmc_pkg1(launch_ctxt_t *ctxt) sdmmc_storage_t storage; sdmmc_t sdmmc; - sdmmc_storage_init_mmc(&storage, &sdmmc, SDMMC_4, SDMMC_BUS_WIDTH_8, 4); + emummc_storage_init_mmc(&storage, &sdmmc); // Read package1. ctxt->pkg1 = (void *)malloc(0x40000); - sdmmc_storage_set_mmc_partition(&storage, 1); - sdmmc_storage_read(&storage, 0x100000 / NX_EMMC_BLOCKSIZE, 0x40000 / NX_EMMC_BLOCKSIZE, ctxt->pkg1); + emummc_storage_set_mmc_partition(&storage, 1); + emummc_storage_read(&storage, 0x100000 / NX_EMMC_BLOCKSIZE, 0x40000 / NX_EMMC_BLOCKSIZE, ctxt->pkg1); ctxt->pkg1_id = pkg1_identify(ctxt->pkg1); if (!ctxt->pkg1_id) { @@ -312,7 +314,7 @@ static int _read_emmc_pkg1(launch_ctxt_t *ctxt) // Read the correct keyblob. ctxt->keyblob = (u8 *)calloc(NX_EMMC_BLOCKSIZE, 1); - sdmmc_storage_read(&storage, 0x180000 / NX_EMMC_BLOCKSIZE + ctxt->pkg1_id->kb, 1, ctxt->keyblob); + emummc_storage_read(&storage, 0x180000 / NX_EMMC_BLOCKSIZE + ctxt->pkg1_id->kb, 1, ctxt->keyblob); res = 1; @@ -327,9 +329,9 @@ static u8 *_read_emmc_pkg2(launch_ctxt_t *ctxt) sdmmc_storage_t storage; sdmmc_t sdmmc; - if (!sdmmc_storage_init_mmc(&storage, &sdmmc, SDMMC_4, SDMMC_BUS_WIDTH_8, 4)) + if (!emummc_storage_init_mmc(&storage, &sdmmc)) return NULL; - sdmmc_storage_set_mmc_partition(&storage, 0); + emummc_storage_set_mmc_partition(&storage, 0); // Parse eMMC GPT. LIST_INIT(gpt); @@ -408,6 +410,10 @@ int hos_launch(ini_sec_t *cfg) return 0; } + // Enable emummc patching. + if (emu_cfg.enabled && !h_cfg.emummc_force_disable) + config_kip1patch(&ctxt, "emummc"); + // Check if fuses lower than 4.0.0 and if yes apply NO Gamecard patch. if (h_cfg.autonogc && !(fuse_read_odm(7) & ~0xF) && ctxt.pkg1_id->kb >= KB_FIRMWARE_VERSION_400) config_kip1patch(&ctxt, "nogc"); diff --git a/bootloader/hos/hos.h b/bootloader/hos/hos.h index 0a78232..e5c7dde 100644 --- a/bootloader/hos/hos.h +++ b/bootloader/hos/hos.h @@ -61,6 +61,7 @@ typedef struct _launch_ctxt_t bool debugmode; bool stock; bool atmosphere; + bool emuMMC; ini_sec_t *cfg; } launch_ctxt_t; diff --git a/bootloader/hos/pkg2.c b/bootloader/hos/pkg2.c index b01c85c..afdd48d 100644 --- a/bootloader/hos/pkg2.c +++ b/bootloader/hos/pkg2.c @@ -19,9 +19,12 @@ #include #include "pkg2.h" +#include "../config/config.h" +#include "../libs/fatfs/ff.h" #include "../utils/aarch64_util.h" #include "../mem/heap.h" #include "../sec/se.h" +#include "../storage/emummc.h" #include "../libs/compr/blz.h" #include "../gfx/gfx.h" @@ -625,9 +628,11 @@ static kip1_id_t _kip_ids[] = const pkg2_kernel_id_t *pkg2_identify(u8 *hash) { - for (u32 i = 0; sizeof(_pkg2_kernel_ids) / sizeof(pkg2_kernel_id_t); i++) + for (u32 i = 0; i < (sizeof(_pkg2_kernel_ids) / sizeof(pkg2_kernel_id_t)); i++) + { if (!memcmp(hash, _pkg2_kernel_ids[i].hash, sizeof(_pkg2_kernel_ids[0].hash))) return &_pkg2_kernel_ids[i]; + } return NULL; } @@ -678,6 +683,7 @@ int pkg2_has_kip(link_t *info, u64 tid) void pkg2_replace_kip(link_t *info, u64 tid, pkg2_kip1_t *kip1) { LIST_FOREACH_ENTRY(pkg2_kip1_info_t, ki, info, link) + { if (ki->kip1->tid == tid) { ki->kip1 = kip1; @@ -685,6 +691,7 @@ void pkg2_replace_kip(link_t *info, u64 tid, pkg2_kip1_t *kip1) DPRINTF("replaced kip (new size %08X)\n", ki->size); return; } + } } void pkg2_add_kip(link_t *info, pkg2_kip1_t *kip1) @@ -773,6 +780,69 @@ int pkg2_decompress_kip(pkg2_kip1_info_t* ki, u32 sectsToDecomp) return 0; } +static int _kipm_inject(const char *kipm_path, char *target_name, pkg2_kip1_info_t* ki) +{ + if (!strcmp((const char *)ki->kip1->name, target_name)) + { + u32 size = 0; + u8 *kipm_data = (u8 *)sd_file_read(kipm_path, &size); + if (!kipm_data) + return 1; + + u32 inject_size = size - sizeof(ki->kip1->caps); + u8 *kip_patched_data = (u8 *)malloc(ki->size + inject_size); + + // Copy headers. + memcpy(kip_patched_data, ki->kip1, sizeof(pkg2_kip1_t)); + + pkg2_kip1_t *fs_kip = ki->kip1; + ki->kip1 = (pkg2_kip1_t *)kip_patched_data; + ki->size = ki->size + inject_size; + + // Patch caps. + memcpy(&ki->kip1->caps, kipm_data, sizeof(ki->kip1->caps)); + // Copy our .text data. + memcpy(&ki->kip1->data, kipm_data + sizeof(ki->kip1->caps), inject_size); + + u32 new_offset = 0; + + for (u32 currSectIdx = 0; currSectIdx < KIP1_NUM_SECTIONS - 2; currSectIdx++) + { + if(!currSectIdx) // .text. + { + memcpy(ki->kip1->data + inject_size, fs_kip->data + new_offset, fs_kip->sections[0].size_comp); + ki->kip1->sections[0].size_decomp += inject_size; + ki->kip1->sections[0].size_comp += inject_size; + } + else // Others. + { + if (currSectIdx < 3) + memcpy(ki->kip1->data + new_offset + inject_size, fs_kip->data + new_offset, fs_kip->sections[currSectIdx].size_comp); + ki->kip1->sections[currSectIdx].offset += inject_size; + } + new_offset += fs_kip->sections[currSectIdx].size_comp; + } + + // Patch PMC capabilities for 1.0.0. + if (!emu_cfg.fs_ver) + { + for (u32 i = 0; i < 0x20; i++) + { + if (ki->kip1->caps[i] == 0xFFFFFFFF) + { + ki->kip1->caps[i] = 0x07000E7F; + break; + } + } + } + + free(kipm_data); + return 0; + } + + return 1; +} + const char* pkg2_patch_kips(link_t *info, char* patchNames) { if (patchNames == NULL || patchNames[0] == 0) @@ -814,7 +884,7 @@ const char* pkg2_patch_kips(link_t *info, char* patchNames) continue; // Eliminate trailing spaces. - for (int chIdx=valueLen - 1; chIdx >= 0; chIdx--) + for (int chIdx = valueLen - 1; chIdx >= 0; chIdx--) { const char* p = patches[i] + chIdx; if (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') @@ -876,7 +946,10 @@ const char* pkg2_patch_kips(link_t *info, char* patchNames) if (strcmp(currPatchset->name, patches[currEnabIdx])) continue; - for (const kip1_patch_t* currPatch=currPatchset->patches; currPatch != NULL && currPatch->length != 0; currPatch++) + if (!strcmp(currPatchset->name, "emummc")) + bitsAffected |= 1u << GET_KIP_PATCH_SECTION(currPatchset->patches->offset); + + for (const kip1_patch_t* currPatch=currPatchset->patches; currPatch != NULL && (currPatch->length != 0); currPatch++) bitsAffected |= 1u << GET_KIP_PATCH_SECTION(currPatch->offset); } } @@ -899,6 +972,7 @@ const char* pkg2_patch_kips(link_t *info, char* patchNames) #endif currPatchset = _kip_ids[currKipIdx].patchset; + bool emummc_patch_selected = false; while (currPatchset != NULL && currPatchset->name != NULL) { for (u32 currEnabIdx = 0; currEnabIdx < numPatches; currEnabIdx++) @@ -907,10 +981,13 @@ const char* pkg2_patch_kips(link_t *info, char* patchNames) continue; u32 appliedMask = 1u << currEnabIdx; - if (currPatchset->patches == NULL) + if (currPatchset->patches == NULL || !strcmp(currPatchset->name, "emummc")) { gfx_printf("Patch '%s' not necessary for %s KIP1\n", currPatchset->name, (const char*)ki->kip1->name); patchesApplied |= appliedMask; + + if (!strcmp(currPatchset->name, "emummc")) + emummc_patch_selected = true; break; } @@ -920,7 +997,7 @@ const char* pkg2_patch_kips(link_t *info, char* patchNames) if (bitsAffected & (1u << currSectIdx)) { gfx_printf("Applying patch '%s' on %s KIP1 sect %d\n", currPatchset->name, (const char*)ki->kip1->name, currSectIdx); - for (const kip1_patch_t* currPatch=currPatchset->patches;currPatch != NULL && currPatch->length != 0; currPatch++) + for (const kip1_patch_t* currPatch = currPatchset->patches; currPatch != NULL && currPatch->length != 0; currPatch++) { if (GET_KIP_PATCH_SECTION(currPatch->offset) != currSectIdx) continue; @@ -946,6 +1023,19 @@ const char* pkg2_patch_kips(link_t *info, char* patchNames) } currPatchset++; } + if (!strncmp(_kip_ids[currKipIdx].name, "FS", 2) && emummc_patch_selected) + { + emummc_patch_selected = false; + emu_cfg.fs_ver = currKipIdx; + if (currKipIdx) + emu_cfg.fs_ver--; + if (currKipIdx > 19) + emu_cfg.fs_ver -= 2; + + gfx_printf("Injecting emuMMC. FS ver: %d\n", emu_cfg.fs_ver); + if (_kipm_inject("/bootloader/sys/emummc.kipm", "FS", ki)) + return "emummc"; + } } } @@ -1007,6 +1097,7 @@ DPRINTF("adding kip1 '%s' @ %08X (%08X)\n", ki->kip1->name, (u32)ki->kip1, ki->s ini1_size += ki->size; ini1->num_procs++; } + ini1_size = ALIGN(ini1_size, 4); ini1->size = ini1_size; if (!new_pkg2) { diff --git a/bootloader/hos/secmon_exo.c b/bootloader/hos/secmon_exo.c index c7b8067..223d200 100644 --- a/bootloader/hos/secmon_exo.c +++ b/bootloader/hos/secmon_exo.c @@ -19,31 +19,77 @@ #include #include "hos.h" +#include "../config/config.h" #include "../gfx/di.h" #include "../gfx/gfx.h" #include "../libs/fatfs/ff.h" #include "../mem/heap.h" #include "../soc/fuse.h" +#include "../storage/emummc.h" #include "../storage/sdmmc.h" #include "../utils/btn.h" #include "../utils/util.h" #include "../utils/types.h" +extern hekate_config h_cfg; + extern bool sd_mount(); extern int sd_save_to_file(void *buf, u32 size, const char *filename); +enum emuMMC_Type +{ + emuMMC_None = 0, + emuMMC_Partition, + emuMMC_File, + emuMMC_MAX +}; + +/* "EFS0" */ +#define EMUMMC_MAGIC 0x30534645 +#define EMUMMC_FILE_PATH_MAX 0x80 + +typedef struct +{ + u32 magic; + u32 type; + u32 id; + u32 fs_ver; +} emummc_base_config_t; + +typedef struct +{ + u64 start_sector; +} emummc_partition_config_t; + +typedef struct +{ + char path[EMUMMC_FILE_PATH_MAX]; +} emummc_file_config_t; + +typedef struct +{ + emummc_base_config_t base_cfg; + union + { + emummc_partition_config_t partition_cfg; + emummc_file_config_t file_cfg; + }; + char nintendo_path[EMUMMC_FILE_PATH_MAX]; +} exo_emummc_config_t; + typedef struct _exo_cfg_t { - vu32 magic; - vu32 fwno; - vu32 flags; - vu32 rsvd; + u32 magic; + u32 fwno; + u32 flags; + u32 reserved[5]; + exo_emummc_config_t emummc_cfg; } exo_cfg_t; typedef struct _atm_meta_t { - uint32_t magic; - uint32_t fwno; + u32 magic; + u32 fwno; } wb_cfg_t; // Atmosphère reboot-to-fatal-error. @@ -118,7 +164,7 @@ void config_exosphere(const char *id, u32 kb, void *warmboot, bool stock) exoFlags |= EXO_FLAG_620_KGN; // To avoid problems, make private debug mode always on if not semi-stock. - if (!stock) + if (!stock || (emu_cfg.enabled && !h_cfg.emummc_force_disable)) exoFlags |= EXO_FLAG_DBG_PRIV; // Set mailbox values. @@ -157,6 +203,24 @@ void config_exosphere(const char *id, u32 kb, void *warmboot, bool stock) memcpy(warmboot + 0x10, rsa_mod + 0x10, 0x100); } + + if (emu_cfg.enabled && !h_cfg.emummc_force_disable) + { + exo_cfg->emummc_cfg.base_cfg.magic = EMUMMC_MAGIC; + exo_cfg->emummc_cfg.base_cfg.type = emu_cfg.sector ? emuMMC_Partition : emuMMC_File; + exo_cfg->emummc_cfg.base_cfg.fs_ver = emu_cfg.fs_ver; + exo_cfg->emummc_cfg.base_cfg.id = emu_cfg.id; + + if (emu_cfg.sector) + exo_cfg->emummc_cfg.partition_cfg.start_sector = emu_cfg.sector; + else + strcpy((char *)exo_cfg->emummc_cfg.file_cfg.path, emu_cfg.path); + + if (emu_cfg.nintendo_path) + strcpy((char *)exo_cfg->emummc_cfg.nintendo_path, emu_cfg.nintendo_path); + else + exo_cfg->emummc_cfg.nintendo_path[0] = 0; + } } static const char *get_error_desc(u32 error_desc) diff --git a/bootloader/hos/sept.c b/bootloader/hos/sept.c index 6878c17..384a78b 100644 --- a/bootloader/hos/sept.c +++ b/bootloader/hos/sept.c @@ -25,6 +25,7 @@ #include "../mem/heap.h" #include "../soc/pmc.h" #include "../soc/t210.h" +#include "../storage/emummc.h" #include "../storage/nx_emmc.h" #include "../storage/sdmmc.h" #include "../utils/btn.h" @@ -63,11 +64,14 @@ extern hekate_config h_cfg; extern const volatile ipl_ver_meta_t ipl_ver; extern void *sd_file_read(char *path); -extern void sd_mount(); +extern bool sd_mount(); extern void sd_unmount(); extern bool is_ipl_updated(void *buf); extern void reloc_patcher(u32 payload_dst, u32 payload_src, u32 payload_size); +extern sdmmc_t sd_sdmmc; +extern sdmmc_storage_t sd_storage; + void check_sept() { // Check if non-hekate payload is used for sept and restore it. @@ -82,15 +86,15 @@ void check_sept() sdmmc_storage_t storage; sdmmc_t sdmmc; - if (!sdmmc_storage_init_mmc(&storage, &sdmmc, SDMMC_4, SDMMC_BUS_WIDTH_8, 4)) + if (!emummc_storage_init_mmc(&storage, &sdmmc)) { EPRINTF("Failed to init eMMC."); goto out_free; } - sdmmc_storage_set_mmc_partition(&storage, 1); + emummc_storage_set_mmc_partition(&storage, 1); // Read package1. - sdmmc_storage_read(&storage, 0x100000 / NX_EMMC_BLOCKSIZE, 0x40000 / NX_EMMC_BLOCKSIZE, pkg1); + emummc_storage_read(&storage, 0x100000 / NX_EMMC_BLOCKSIZE, 0x40000 / NX_EMMC_BLOCKSIZE, pkg1); const pkg1_id_t *pkg1_id = pkg1_identify(pkg1); if (!pkg1_id) { diff --git a/bootloader/main.c b/bootloader/main.c index 1ac2a0e..cf4abe2 100644 --- a/bootloader/main.c +++ b/bootloader/main.c @@ -39,6 +39,7 @@ #include "soc/i2c.h" #include "soc/t210.h" #include "soc/uart.h" +#include "storage/emummc.h" #include "storage/nx_emmc.h" #include "storage/sdmmc.h" #include "utils/btn.h" @@ -630,6 +631,15 @@ void launch_firmware() payload_path = ini_check_payload_section(cfg_tmp); + if (cfg_tmp) + { + LIST_FOREACH_ENTRY(ini_kv_t, kv, &cfg_tmp->kvs, link) + { + if (!strcmp("emummc_force_disable", kv->key)) + h_cfg.emummc_force_disable = atoi(kv->val); + } + } + if (cfg_tmp && !payload_path) check_sept(); @@ -675,6 +685,8 @@ out: ini_free_section(cfg_sec); sd_unmount(); + h_cfg.emummc_force_disable = false; + btn_wait(); } @@ -768,6 +780,8 @@ void auto_launch_firmware() { if (!strcmp("logopath", kv->key)) bootlogoCustomEntry = kv->val; + if (!strcmp("emummc_force_disable", kv->key)) + h_cfg.emummc_force_disable = atoi(kv->val); } break; } @@ -795,11 +809,14 @@ void auto_launch_firmware() if (h_cfg.autoboot == boot_entry_id) { + h_cfg.emummc_force_disable = false; cfg_sec = ini_clone_section(ini_sec_list); LIST_FOREACH_ENTRY(ini_kv_t, kv, &cfg_sec->kvs, link) { if (!strcmp("logopath", kv->key)) bootlogoCustomEntry = kv->val; + if (!strcmp("emummc_force_disable", kv->key)) + h_cfg.emummc_force_disable = atoi(kv->val); } break; } @@ -927,10 +944,11 @@ out: ini_free(&ini_list_sections); ini_free_section(cfg_sec); - sd_unmount(); gfx_con.mute = false; b_cfg.boot_cfg &= ~(BOOT_CFG_AUTOBOOT_EN | BOOT_CFG_FROM_LAUNCH); + h_cfg.emummc_force_disable = false; + sd_unmount(); } void patched_rcm_protection() @@ -1179,6 +1197,9 @@ void ipl_main() // Check if RCM is patched and protect from a possible brick. patched_rcm_protection(); + // Load emuMMC configuration from SD. + emummc_load_cfg(); + // Load saved configuration and auto boot if enabled. auto_launch_firmware(); diff --git a/bootloader/storage/emummc.c b/bootloader/storage/emummc.c new file mode 100644 index 0000000..d7eb1e1 --- /dev/null +++ b/bootloader/storage/emummc.c @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2019 CTCaer + * + * 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 . + */ + +#include +#include + +#include "emummc.h" +#include "sdmmc.h" +#include "../config/config.h" +#include "../config/ini.h" +#include "../gfx/gfx.h" +#include "../libs/fatfs/ff.h" +#include "../mem/heap.h" +#include "../utils/list.h" +#include "../utils/types.h" + +extern sdmmc_t sd_sdmmc; +extern sdmmc_storage_t sd_storage; +extern FATFS sd_fs; + +extern hekate_config h_cfg; + +extern bool sd_mount(); +extern void sd_unmount(); + +void emummc_load_cfg() +{ + sd_mount(); + emu_cfg.enabled = 0; + emu_cfg.path = NULL; + emu_cfg.nintendo_path = NULL; + emu_cfg.sector = 0; + emu_cfg.id = 0; + emu_cfg.file_based_part_size = 0; + emu_cfg.active_part = 0; + emu_cfg.fs_ver = 0; + emu_cfg.emummc_file_based_path = (char *)malloc(0x80); + + LIST_INIT(ini_sections); + if (ini_parse(&ini_sections, "emuMMC/emummc.ini", false)) + { + LIST_FOREACH_ENTRY(ini_sec_t, ini_sec, &ini_sections, link) + { + if (ini_sec->type == INI_CHOICE) + { + if (strcmp(ini_sec->name, "emummc")) + continue; + + LIST_FOREACH_ENTRY(ini_kv_t, kv, &ini_sec->kvs, link) + { + if (!strcmp("enabled", kv->key)) + emu_cfg.enabled = atoi(kv->val); + else if (!strcmp("sector", kv->key)) + emu_cfg.sector = strtol(kv->val, NULL, 16); + else if (!strcmp("id", kv->key)) + emu_cfg.id = strtol(kv->val, NULL, 16); + else if (!strcmp("path", kv->key)) + emu_cfg.path = kv->val; + else if (!strcmp("nintendo_path", kv->key)) + emu_cfg.nintendo_path = kv->val; + } + break; + } + } + } +} + +static int emummc_raw_get_part_off(int part_idx) +{ + switch (part_idx) + { + case 0: + return 2; + case 1: + return 0; + case 2: + return 1; + } + return 2; +} + + +int emummc_storage_init_mmc(sdmmc_storage_t *storage, sdmmc_t *sdmmc) +{ + FILINFO fno; + if (!sdmmc_storage_init_mmc(storage, sdmmc, SDMMC_4, SDMMC_BUS_WIDTH_8, 4)) + { + EPRINTF("Failed to init eMMC."); + + goto out; + } + if (h_cfg.emummc_force_disable) + return 1; + + emu_cfg.active_part = 0; + if (!sd_mount()) + goto out; + + if (emu_cfg.enabled && !emu_cfg.sector) + { + strcpy(emu_cfg.emummc_file_based_path, emu_cfg.path); + strcat(emu_cfg.emummc_file_based_path, "/eMMC"); + + if (f_stat(emu_cfg.emummc_file_based_path, &fno)) + { + gfx_printf("e1\n"); + gfx_printf(" %X\n ", emu_cfg.sector); + goto out; + } + f_chmod(emu_cfg.emummc_file_based_path, AM_ARC, AM_ARC); + + strcat(emu_cfg.emummc_file_based_path, "/00"); + if (f_stat(emu_cfg.emummc_file_based_path, &fno)) + { + gfx_printf("e2\n"); + goto out; + } + emu_cfg.file_based_part_size = fno.fsize >> 9; + } + return 1; + +out: + return 0; +} + +int emummc_storage_end(sdmmc_storage_t *storage) +{ + sd_unmount(); + sdmmc_storage_end(storage); + + return 1; +} + +int emummc_storage_read(sdmmc_storage_t *storage, u32 sector, u32 num_sectors, void *buf) +{ + FIL fp; + if (!emu_cfg.enabled || h_cfg.emummc_force_disable) + return sdmmc_storage_read(storage, sector, num_sectors, buf); + else if (emu_cfg.sector) + { + sector += emu_cfg.sector; + sector += emummc_raw_get_part_off(emu_cfg.active_part) * 0x2000; + return sdmmc_storage_read(&sd_storage, sector, num_sectors, buf); + } + else + { + if (!emu_cfg.active_part) + { + u32 file_part = sector / emu_cfg.file_based_part_size; + sector = sector % emu_cfg.file_based_part_size; + if (file_part >= 10) + itoa(file_part, emu_cfg.emummc_file_based_path + strlen(emu_cfg.emummc_file_based_path) - 2, 10); + else + { + emu_cfg.emummc_file_based_path[strlen(emu_cfg.emummc_file_based_path) - 2] = '0'; + itoa(file_part, emu_cfg.emummc_file_based_path + strlen(emu_cfg.emummc_file_based_path) - 1, 10); + } + } + if (f_open(&fp, emu_cfg.emummc_file_based_path, FA_READ)) + { + gfx_printf("e3\n"); + return 0; + } + f_lseek(&fp, (u64)sector << 9); + if (f_read(&fp, buf, (u64)num_sectors << 9, NULL)) + { + gfx_printf("e4\n"); + f_close(&fp); + return 0; + } + + f_close(&fp); + return 1; + } + + return 1; +} + +int emummc_storage_write(sdmmc_storage_t *storage, u32 sector, u32 num_sectors, void *buf) +{ + FIL fp; + if (!emu_cfg.enabled || h_cfg.emummc_force_disable) + return sdmmc_storage_write(storage, sector, num_sectors, buf); + else if (emu_cfg.sector) + { + sector += emu_cfg.sector; + sector += emummc_raw_get_part_off(emu_cfg.active_part) * 0x2000; + return sdmmc_storage_write(&sd_storage, sector, num_sectors, buf); + } + else + { + if (!emu_cfg.active_part) + { + u32 file_part = sector / emu_cfg.file_based_part_size; + sector = sector % emu_cfg.file_based_part_size; + if (file_part >= 10) + itoa(file_part, emu_cfg.emummc_file_based_path + strlen(emu_cfg.emummc_file_based_path) - 2, 10); + else + { + emu_cfg.emummc_file_based_path[strlen(emu_cfg.emummc_file_based_path) - 2] = '0'; + itoa(file_part, emu_cfg.emummc_file_based_path + strlen(emu_cfg.emummc_file_based_path) - 1, 10); + } + } + if (f_open(&fp, emu_cfg.emummc_file_based_path, FA_WRITE)) + { + gfx_printf("e5\n"); + return 0; + } + f_lseek(&fp, (u64)sector << 9); + if (f_write(&fp, buf, (u64)num_sectors << 9, NULL)) + { + gfx_printf("e6\n"); + f_close(&fp); + return 0; + } + + f_close(&fp); + return 1; + } +} + +int emummc_storage_set_mmc_partition(sdmmc_storage_t *storage, u32 partition) +{ + emu_cfg.active_part = partition; + + if (!emu_cfg.enabled || h_cfg.emummc_force_disable) + sdmmc_storage_set_mmc_partition(storage, partition); + else if (emu_cfg.sector) + return 1; + else + { + strcpy(emu_cfg.emummc_file_based_path, emu_cfg.path); + strcat(emu_cfg.emummc_file_based_path, "/eMMC"); + + switch (partition) + { + case 0: + strcat(emu_cfg.emummc_file_based_path, "/00"); + break; + case 1: + strcat(emu_cfg.emummc_file_based_path, "/BOOT0"); + break; + case 2: + strcat(emu_cfg.emummc_file_based_path, "/BOOT1"); + break; + } + + return 1; + } + + return 1; +} diff --git a/bootloader/storage/emummc.h b/bootloader/storage/emummc.h new file mode 100644 index 0000000..437d3ed --- /dev/null +++ b/bootloader/storage/emummc.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2019 CTCaer + * + * 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 . + */ + +#ifndef EMUMMC_H +#define EMUMMC_H + +#include "sdmmc.h" +#include "../utils/types.h" + +typedef enum +{ + EMUMMC_TYPE_NONE = 0, + EMUMMC_TYPE_PARTITION = 1, + EMUMMC_TYPE_FILES = 2, +} emummc_type_t; + +typedef enum { + EMUMMC_MMC_NAND = 0, + EMUMMC_MMC_SD = 1, + EMUMMC_MMC_GC = 2, +} emummc_mmc_t; + +typedef struct _emummc_cfg_t +{ + int enabled; + u64 sector; + u16 id; + char *path; + char *nintendo_path; + // Internal. + char *emummc_file_based_path; + u32 file_based_part_size; + u32 active_part; + int fs_ver; +} emummc_cfg_t; + +emummc_cfg_t emu_cfg; + +void emummc_load_cfg(); +int emummc_storage_init_mmc(sdmmc_storage_t *storage, sdmmc_t *sdmmc); +int emummc_storage_end(sdmmc_storage_t *storage); +int emummc_storage_read(sdmmc_storage_t *storage, u32 sector, u32 num_sectors, void *buf); +int emummc_storage_write(sdmmc_storage_t *storage, u32 sector, u32 num_sectors, void *buf); +int emummc_storage_set_mmc_partition(sdmmc_storage_t *storage, u32 partition); + +#endif \ No newline at end of file diff --git a/bootloader/storage/nx_emmc.c b/bootloader/storage/nx_emmc.c index a65dced..8e0fb63 100644 --- a/bootloader/storage/nx_emmc.c +++ b/bootloader/storage/nx_emmc.c @@ -17,6 +17,7 @@ #include #include "nx_emmc.h" +#include "emummc.h" #include "../mem/heap.h" #include "../utils/list.h" @@ -24,7 +25,7 @@ void nx_emmc_gpt_parse(link_t *gpt, sdmmc_storage_t *storage) { u8 *buf = (u8 *)malloc(NX_GPT_NUM_BLOCKS * NX_EMMC_BLOCKSIZE); - sdmmc_storage_read(storage, NX_GPT_FIRST_LBA, NX_GPT_NUM_BLOCKS, buf); + emummc_storage_read(storage, NX_GPT_FIRST_LBA, NX_GPT_NUM_BLOCKS, buf); gpt_header_t *hdr = (gpt_header_t *)buf; for (u32 i = 0; i < hdr->num_part_ents; i++) @@ -65,7 +66,7 @@ int nx_emmc_part_read(sdmmc_storage_t *storage, emmc_part_t *part, u32 sector_of // The last LBA is inclusive. if (part->lba_start + sector_off > part->lba_end) return 0; - return sdmmc_storage_read(storage, part->lba_start + sector_off, num_sectors, buf); + return emummc_storage_read(storage, part->lba_start + sector_off, num_sectors, buf); } int nx_emmc_part_write(sdmmc_storage_t *storage, emmc_part_t *part, u32 sector_off, u32 num_sectors, void *buf)