[emuMMC] Add support

This commit is contained in:
ctcaer@gmail.com 2019-06-30 03:24:58 +03:00
parent 8101fd3f7f
commit bd7f572989
11 changed files with 544 additions and 26 deletions

View file

@ -995,3 +995,5 @@ out:
void restore_emmc_boot() { _restore_emmc_selected(PART_BOOT); } void restore_emmc_boot() { _restore_emmc_selected(PART_BOOT); }
void restore_emmc_rawnand() { _restore_emmc_selected(PART_RAW); } void restore_emmc_rawnand() { _restore_emmc_selected(PART_RAW); }
void restore_emmc_gpp_parts() { _restore_emmc_selected(PART_GP_ALL); } void restore_emmc_gpp_parts() { _restore_emmc_selected(PART_GP_ALL); }
#pragma GCC pop_options

View file

@ -22,10 +22,13 @@
#include "hos.h" #include "hos.h"
#include "../libs/fatfs/ff.h" #include "../libs/fatfs/ff.h"
#include "../mem/heap.h" #include "../mem/heap.h"
#include "../storage/emummc.h"
#include "../gfx/gfx.h" #include "../gfx/gfx.h"
#define DPRINTF(...) #define DPRINTF(...)
extern hekate_config h_cfg;
#define FSS0_MAGIC 0x30535346 #define FSS0_MAGIC 0x30535346
#define CNT_TYPE_FSP 0 #define CNT_TYPE_FSP 0
#define CNT_TYPE_EXO 1 #define CNT_TYPE_EXO 1
@ -35,6 +38,7 @@
#define CNT_TYPE_SP2 5 #define CNT_TYPE_SP2 5
#define CNT_TYPE_KIP 6 #define CNT_TYPE_KIP 6
#define CNT_TYPE_BMP 7 #define CNT_TYPE_BMP 7
#define CNT_TYPE_EMC 8
typedef struct _fss_t typedef struct _fss_t
{ {
@ -70,7 +74,7 @@ int parse_fss(launch_ctxt_t *ctxt, const char *value)
stock = true; 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; return 1;
if (f_open(&fp, value, FA_READ) != FR_OK) if (f_open(&fp, value, FA_READ) != FR_OK)

View file

@ -36,6 +36,7 @@
#include "../soc/pmc.h" #include "../soc/pmc.h"
#include "../soc/smmu.h" #include "../soc/smmu.h"
#include "../soc/t210.h" #include "../soc/t210.h"
#include "../storage/emummc.h"
#include "../storage/nx_emmc.h" #include "../storage/nx_emmc.h"
#include "../storage/sdmmc.h" #include "../storage/sdmmc.h"
#include "../utils/util.h" #include "../utils/util.h"
@ -44,6 +45,7 @@
extern hekate_config h_cfg; extern hekate_config h_cfg;
extern void sd_unmount(); extern void sd_unmount();
extern bool sd_mount();
//#define DPRINTF(...) gfx_printf(__VA_ARGS__) //#define DPRINTF(...) gfx_printf(__VA_ARGS__)
#define DPRINTF(...) #define DPRINTF(...)
@ -296,12 +298,12 @@ static int _read_emmc_pkg1(launch_ctxt_t *ctxt)
sdmmc_storage_t storage; sdmmc_storage_t storage;
sdmmc_t sdmmc; sdmmc_t sdmmc;
sdmmc_storage_init_mmc(&storage, &sdmmc, SDMMC_4, SDMMC_BUS_WIDTH_8, 4); emummc_storage_init_mmc(&storage, &sdmmc);
// Read package1. // Read package1.
ctxt->pkg1 = (void *)malloc(0x40000); ctxt->pkg1 = (void *)malloc(0x40000);
sdmmc_storage_set_mmc_partition(&storage, 1); emummc_storage_set_mmc_partition(&storage, 1);
sdmmc_storage_read(&storage, 0x100000 / NX_EMMC_BLOCKSIZE, 0x40000 / NX_EMMC_BLOCKSIZE, ctxt->pkg1); emummc_storage_read(&storage, 0x100000 / NX_EMMC_BLOCKSIZE, 0x40000 / NX_EMMC_BLOCKSIZE, ctxt->pkg1);
ctxt->pkg1_id = pkg1_identify(ctxt->pkg1); ctxt->pkg1_id = pkg1_identify(ctxt->pkg1);
if (!ctxt->pkg1_id) if (!ctxt->pkg1_id)
{ {
@ -312,7 +314,7 @@ static int _read_emmc_pkg1(launch_ctxt_t *ctxt)
// Read the correct keyblob. // Read the correct keyblob.
ctxt->keyblob = (u8 *)calloc(NX_EMMC_BLOCKSIZE, 1); 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; res = 1;
@ -327,9 +329,9 @@ static u8 *_read_emmc_pkg2(launch_ctxt_t *ctxt)
sdmmc_storage_t storage; sdmmc_storage_t storage;
sdmmc_t sdmmc; 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; return NULL;
sdmmc_storage_set_mmc_partition(&storage, 0); emummc_storage_set_mmc_partition(&storage, 0);
// Parse eMMC GPT. // Parse eMMC GPT.
LIST_INIT(gpt); LIST_INIT(gpt);
@ -408,6 +410,10 @@ int hos_launch(ini_sec_t *cfg)
return 0; 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. // 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) if (h_cfg.autonogc && !(fuse_read_odm(7) & ~0xF) && ctxt.pkg1_id->kb >= KB_FIRMWARE_VERSION_400)
config_kip1patch(&ctxt, "nogc"); config_kip1patch(&ctxt, "nogc");

View file

@ -61,6 +61,7 @@ typedef struct _launch_ctxt_t
bool debugmode; bool debugmode;
bool stock; bool stock;
bool atmosphere; bool atmosphere;
bool emuMMC;
ini_sec_t *cfg; ini_sec_t *cfg;
} launch_ctxt_t; } launch_ctxt_t;

View file

@ -19,9 +19,12 @@
#include <string.h> #include <string.h>
#include "pkg2.h" #include "pkg2.h"
#include "../config/config.h"
#include "../libs/fatfs/ff.h"
#include "../utils/aarch64_util.h" #include "../utils/aarch64_util.h"
#include "../mem/heap.h" #include "../mem/heap.h"
#include "../sec/se.h" #include "../sec/se.h"
#include "../storage/emummc.h"
#include "../libs/compr/blz.h" #include "../libs/compr/blz.h"
#include "../gfx/gfx.h" #include "../gfx/gfx.h"
@ -625,9 +628,11 @@ static kip1_id_t _kip_ids[] =
const pkg2_kernel_id_t *pkg2_identify(u8 *hash) 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))) if (!memcmp(hash, _pkg2_kernel_ids[i].hash, sizeof(_pkg2_kernel_ids[0].hash)))
return &_pkg2_kernel_ids[i]; return &_pkg2_kernel_ids[i];
}
return NULL; 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) void pkg2_replace_kip(link_t *info, u64 tid, pkg2_kip1_t *kip1)
{ {
LIST_FOREACH_ENTRY(pkg2_kip1_info_t, ki, info, link) LIST_FOREACH_ENTRY(pkg2_kip1_info_t, ki, info, link)
{
if (ki->kip1->tid == tid) if (ki->kip1->tid == tid)
{ {
ki->kip1 = kip1; 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); DPRINTF("replaced kip (new size %08X)\n", ki->size);
return; return;
} }
}
} }
void pkg2_add_kip(link_t *info, pkg2_kip1_t *kip1) 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; 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) const char* pkg2_patch_kips(link_t *info, char* patchNames)
{ {
if (patchNames == NULL || patchNames[0] == 0) if (patchNames == NULL || patchNames[0] == 0)
@ -814,7 +884,7 @@ const char* pkg2_patch_kips(link_t *info, char* patchNames)
continue; continue;
// Eliminate trailing spaces. // 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; const char* p = patches[i] + chIdx;
if (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') 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])) if (strcmp(currPatchset->name, patches[currEnabIdx]))
continue; 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); bitsAffected |= 1u << GET_KIP_PATCH_SECTION(currPatch->offset);
} }
} }
@ -899,6 +972,7 @@ const char* pkg2_patch_kips(link_t *info, char* patchNames)
#endif #endif
currPatchset = _kip_ids[currKipIdx].patchset; currPatchset = _kip_ids[currKipIdx].patchset;
bool emummc_patch_selected = false;
while (currPatchset != NULL && currPatchset->name != NULL) while (currPatchset != NULL && currPatchset->name != NULL)
{ {
for (u32 currEnabIdx = 0; currEnabIdx < numPatches; currEnabIdx++) for (u32 currEnabIdx = 0; currEnabIdx < numPatches; currEnabIdx++)
@ -907,10 +981,13 @@ const char* pkg2_patch_kips(link_t *info, char* patchNames)
continue; continue;
u32 appliedMask = 1u << currEnabIdx; 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); gfx_printf("Patch '%s' not necessary for %s KIP1\n", currPatchset->name, (const char*)ki->kip1->name);
patchesApplied |= appliedMask; patchesApplied |= appliedMask;
if (!strcmp(currPatchset->name, "emummc"))
emummc_patch_selected = true;
break; break;
} }
@ -920,7 +997,7 @@ const char* pkg2_patch_kips(link_t *info, char* patchNames)
if (bitsAffected & (1u << currSectIdx)) if (bitsAffected & (1u << currSectIdx))
{ {
gfx_printf("Applying patch '%s' on %s KIP1 sect %d\n", currPatchset->name, (const char*)ki->kip1->name, 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) if (GET_KIP_PATCH_SECTION(currPatch->offset) != currSectIdx)
continue; continue;
@ -946,6 +1023,19 @@ const char* pkg2_patch_kips(link_t *info, char* patchNames)
} }
currPatchset++; 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_size += ki->size;
ini1->num_procs++; ini1->num_procs++;
} }
ini1_size = ALIGN(ini1_size, 4);
ini1->size = ini1_size; ini1->size = ini1_size;
if (!new_pkg2) if (!new_pkg2)
{ {

View file

@ -19,31 +19,77 @@
#include <stdlib.h> #include <stdlib.h>
#include "hos.h" #include "hos.h"
#include "../config/config.h"
#include "../gfx/di.h" #include "../gfx/di.h"
#include "../gfx/gfx.h" #include "../gfx/gfx.h"
#include "../libs/fatfs/ff.h" #include "../libs/fatfs/ff.h"
#include "../mem/heap.h" #include "../mem/heap.h"
#include "../soc/fuse.h" #include "../soc/fuse.h"
#include "../storage/emummc.h"
#include "../storage/sdmmc.h" #include "../storage/sdmmc.h"
#include "../utils/btn.h" #include "../utils/btn.h"
#include "../utils/util.h" #include "../utils/util.h"
#include "../utils/types.h" #include "../utils/types.h"
extern hekate_config h_cfg;
extern bool sd_mount(); extern bool sd_mount();
extern int sd_save_to_file(void *buf, u32 size, const char *filename); 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 typedef struct _exo_cfg_t
{ {
vu32 magic; u32 magic;
vu32 fwno; u32 fwno;
vu32 flags; u32 flags;
vu32 rsvd; u32 reserved[5];
exo_emummc_config_t emummc_cfg;
} exo_cfg_t; } exo_cfg_t;
typedef struct _atm_meta_t typedef struct _atm_meta_t
{ {
uint32_t magic; u32 magic;
uint32_t fwno; u32 fwno;
} wb_cfg_t; } wb_cfg_t;
// Atmosphère reboot-to-fatal-error. // 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; exoFlags |= EXO_FLAG_620_KGN;
// To avoid problems, make private debug mode always on if not semi-stock. // 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; exoFlags |= EXO_FLAG_DBG_PRIV;
// Set mailbox values. // 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); 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) static const char *get_error_desc(u32 error_desc)

View file

@ -25,6 +25,7 @@
#include "../mem/heap.h" #include "../mem/heap.h"
#include "../soc/pmc.h" #include "../soc/pmc.h"
#include "../soc/t210.h" #include "../soc/t210.h"
#include "../storage/emummc.h"
#include "../storage/nx_emmc.h" #include "../storage/nx_emmc.h"
#include "../storage/sdmmc.h" #include "../storage/sdmmc.h"
#include "../utils/btn.h" #include "../utils/btn.h"
@ -63,11 +64,14 @@ extern hekate_config h_cfg;
extern const volatile ipl_ver_meta_t ipl_ver; extern const volatile ipl_ver_meta_t ipl_ver;
extern void *sd_file_read(char *path); extern void *sd_file_read(char *path);
extern void sd_mount(); extern bool sd_mount();
extern void sd_unmount(); extern void sd_unmount();
extern bool is_ipl_updated(void *buf); extern bool is_ipl_updated(void *buf);
extern void reloc_patcher(u32 payload_dst, u32 payload_src, u32 payload_size); 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() void check_sept()
{ {
// Check if non-hekate payload is used for sept and restore it. // Check if non-hekate payload is used for sept and restore it.
@ -82,15 +86,15 @@ void check_sept()
sdmmc_storage_t storage; sdmmc_storage_t storage;
sdmmc_t sdmmc; 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."); EPRINTF("Failed to init eMMC.");
goto out_free; goto out_free;
} }
sdmmc_storage_set_mmc_partition(&storage, 1); emummc_storage_set_mmc_partition(&storage, 1);
// Read package1. // 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); const pkg1_id_t *pkg1_id = pkg1_identify(pkg1);
if (!pkg1_id) if (!pkg1_id)
{ {

View file

@ -39,6 +39,7 @@
#include "soc/i2c.h" #include "soc/i2c.h"
#include "soc/t210.h" #include "soc/t210.h"
#include "soc/uart.h" #include "soc/uart.h"
#include "storage/emummc.h"
#include "storage/nx_emmc.h" #include "storage/nx_emmc.h"
#include "storage/sdmmc.h" #include "storage/sdmmc.h"
#include "utils/btn.h" #include "utils/btn.h"
@ -630,6 +631,15 @@ void launch_firmware()
payload_path = ini_check_payload_section(cfg_tmp); 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) if (cfg_tmp && !payload_path)
check_sept(); check_sept();
@ -675,6 +685,8 @@ out:
ini_free_section(cfg_sec); ini_free_section(cfg_sec);
sd_unmount(); sd_unmount();
h_cfg.emummc_force_disable = false;
btn_wait(); btn_wait();
} }
@ -768,6 +780,8 @@ void auto_launch_firmware()
{ {
if (!strcmp("logopath", kv->key)) if (!strcmp("logopath", kv->key))
bootlogoCustomEntry = kv->val; bootlogoCustomEntry = kv->val;
if (!strcmp("emummc_force_disable", kv->key))
h_cfg.emummc_force_disable = atoi(kv->val);
} }
break; break;
} }
@ -795,11 +809,14 @@ void auto_launch_firmware()
if (h_cfg.autoboot == boot_entry_id) if (h_cfg.autoboot == boot_entry_id)
{ {
h_cfg.emummc_force_disable = false;
cfg_sec = ini_clone_section(ini_sec_list); cfg_sec = ini_clone_section(ini_sec_list);
LIST_FOREACH_ENTRY(ini_kv_t, kv, &cfg_sec->kvs, link) LIST_FOREACH_ENTRY(ini_kv_t, kv, &cfg_sec->kvs, link)
{ {
if (!strcmp("logopath", kv->key)) if (!strcmp("logopath", kv->key))
bootlogoCustomEntry = kv->val; bootlogoCustomEntry = kv->val;
if (!strcmp("emummc_force_disable", kv->key))
h_cfg.emummc_force_disable = atoi(kv->val);
} }
break; break;
} }
@ -927,10 +944,11 @@ out:
ini_free(&ini_list_sections); ini_free(&ini_list_sections);
ini_free_section(cfg_sec); ini_free_section(cfg_sec);
sd_unmount();
gfx_con.mute = false; gfx_con.mute = false;
b_cfg.boot_cfg &= ~(BOOT_CFG_AUTOBOOT_EN | BOOT_CFG_FROM_LAUNCH); b_cfg.boot_cfg &= ~(BOOT_CFG_AUTOBOOT_EN | BOOT_CFG_FROM_LAUNCH);
h_cfg.emummc_force_disable = false;
sd_unmount();
} }
void patched_rcm_protection() void patched_rcm_protection()
@ -1179,6 +1197,9 @@ void ipl_main()
// Check if RCM is patched and protect from a possible brick. // Check if RCM is patched and protect from a possible brick.
patched_rcm_protection(); patched_rcm_protection();
// Load emuMMC configuration from SD.
emummc_load_cfg();
// Load saved configuration and auto boot if enabled. // Load saved configuration and auto boot if enabled.
auto_launch_firmware(); auto_launch_firmware();

265
bootloader/storage/emummc.c Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <stdlib.h>
#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;
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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

View file

@ -17,6 +17,7 @@
#include <string.h> #include <string.h>
#include "nx_emmc.h" #include "nx_emmc.h"
#include "emummc.h"
#include "../mem/heap.h" #include "../mem/heap.h"
#include "../utils/list.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); 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; gpt_header_t *hdr = (gpt_header_t *)buf;
for (u32 i = 0; i < hdr->num_part_ents; i++) 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. // The last LBA is inclusive.
if (part->lba_start + sector_off > part->lba_end) if (part->lba_start + sector_off > part->lba_end)
return 0; 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) int nx_emmc_part_write(sdmmc_storage_t *storage, emmc_part_t *part, u32 sector_off, u32 num_sectors, void *buf)