7dbded99a2
- Gotten rid of the patched FIRMs, AuReiNand now finds and loads all the FIRMs from CTRNAND by default. If you are booting an emuNAND, the FIRMs will be loaded from its CTRNAND. This also applies to AGB and TWL FIRM, and allows for a very fast boot with no firmware files on the SD card. - If for some reason (like using NTR) you do not want to use the CTRNAND FIRM, you can place a firmware.bin in the aurei folder and it will be loaded just for the default NAND. - The way AuReiNand works has changed. Now you can specify to autoboot SysNAND or not, and a NAND is no more tied to a FIRM (since 9.0 FIRM is autodetected). If you press nothing the default NAND is booted with its own FIRM, L boots the non-default NAND with its own FIRM, R boots EmuNAND with the SysNAND FIRM if you picked "Updated SysNAND", and vice-versa. - In order for AuReiNand to handle FIRM reboots, the .bin path needs to be hardcoded in the program. The default is /arm9loaderhax.bin (the AuReiNand.dat is also supported for 9.0 people). A PC tool was written to make changing the path easier. - Bug fixes and stuff I forgot. - Gelex is a saint.
417 lines
13 KiB
C
Executable File
417 lines
13 KiB
C
Executable File
/*
|
|
* firm.c
|
|
* by Reisyukaku / Aurora Wright
|
|
* Copyright (c) 2016 All Rights Reserved
|
|
*/
|
|
|
|
#include "firm.h"
|
|
#include "config.h"
|
|
#include "utils.h"
|
|
#include "fs.h"
|
|
#include "patches.h"
|
|
#include "memory.h"
|
|
#include "emunand.h"
|
|
#include "crypto.h"
|
|
#include "draw.h"
|
|
#include "screeninit.h"
|
|
#include "loader.h"
|
|
#include "buttons.h"
|
|
#include "../build/patches.h"
|
|
|
|
static firmHeader *const firm = (firmHeader *)0x24000000;
|
|
static const firmSectionHeader *section;
|
|
|
|
static const char *firmFolders[3][2] = {{ "00000002", "20000002" },
|
|
{ "00000102", "20000102" },
|
|
{ "00000202", "20000202" }};
|
|
u32 config,
|
|
console,
|
|
emuOffset,
|
|
firmSource;
|
|
|
|
void main(void)
|
|
{
|
|
u32 bootType,
|
|
firmType,
|
|
nandType,
|
|
a9lhInstalled,
|
|
updatedSys,
|
|
needConfig,
|
|
newConfig,
|
|
emuHeader;
|
|
|
|
//Detect the console being used
|
|
console = (PDN_MPCORE_CFG == 1) ? 0 : 1;
|
|
|
|
//Mount filesystems. CTRNAND will be mounted only if/when needed
|
|
mountFs();
|
|
|
|
//Attempt to read the configuration file
|
|
const char configPath[] = "/aurei/config.bin";
|
|
if(fileRead(&config, configPath, 3)) needConfig = 1;
|
|
else
|
|
{
|
|
config = 0;
|
|
needConfig = 2;
|
|
}
|
|
|
|
//Determine if this is a firmlaunch boot
|
|
if(*(vu8 *)0x23F00005)
|
|
{
|
|
bootType = 1;
|
|
|
|
//'0' = NATIVE_FIRM, '1' = TWL_FIRM, '2' = AGB_FIRM
|
|
firmType = *(vu8 *)0x23F00005 - 0x2F;
|
|
|
|
nandType = CONFIG(16, 3);
|
|
firmSource = CONFIG(18, 1);
|
|
a9lhInstalled = CONFIG(19, 1);
|
|
updatedSys = (a9lhInstalled && CONFIG(1, 1)) ? 1 : 0;
|
|
}
|
|
else
|
|
{
|
|
bootType = 0;
|
|
firmType = 1;
|
|
|
|
//Determine if booting with A9LH
|
|
u32 a9lhBoot = !PDN_SPI_CNT ? 1 : 0;
|
|
|
|
//Retrieve the last booted FIRM
|
|
u32 previousFirm = CFG_BOOTENV;
|
|
|
|
//Get pressed buttons
|
|
u32 pressed = HID_PAD;
|
|
|
|
//Determine if we need to autoboot sysNAND
|
|
u32 autoBootSys = CONFIG(0, 1);
|
|
|
|
//Determine if A9LH is installed and the user has an updated sysNAND
|
|
if(a9lhBoot || CONFIG(2, 1))
|
|
{
|
|
if(pressed == SAFE_MODE)
|
|
error("Using Safe Mode would brick you, or remove A9LH!");
|
|
|
|
a9lhInstalled = 1;
|
|
|
|
//Check setting for > 9.2 sysNAND
|
|
updatedSys = CONFIG(1, 1);
|
|
}
|
|
else
|
|
{
|
|
a9lhInstalled = 0;
|
|
updatedSys = 0;
|
|
}
|
|
|
|
newConfig = a9lhInstalled << 19;
|
|
|
|
/* If booting with A9LH, it's a MCU reboot and a previous configuration exists,
|
|
try to force boot options */
|
|
if(a9lhBoot && previousFirm && needConfig == 1)
|
|
{
|
|
//Always force a sysNAND boot when quitting AGB_FIRM
|
|
if(previousFirm == 7)
|
|
{
|
|
nandType = 0;
|
|
firmSource = updatedSys ? 0 : CONFIG(18, 1);
|
|
needConfig = 0;
|
|
|
|
//Flag to prevent multiple boot options-forcing
|
|
newConfig |= 1 << 20;
|
|
}
|
|
/* Else, force the last used boot options unless a payload button or A/L/R are pressed
|
|
or the no-forcing flag is set */
|
|
else if(!(pressed & OVERRIDE_BUTTONS) && !CONFIG(20, 1))
|
|
{
|
|
nandType = CONFIG(16, 3);
|
|
firmSource = CONFIG(18, 1);
|
|
needConfig = 0;
|
|
}
|
|
}
|
|
|
|
//Boot options aren't being forced
|
|
if(needConfig)
|
|
{
|
|
/* If L and R/Select or one of the single payload buttons are pressed and, if not using A9LH,
|
|
the Safe Mode combo is not pressed, chainload an external payload */
|
|
if(((pressed & SINGLE_PAYLOAD_BUTTONS) || ((pressed & BUTTON_L1) && (pressed & L_PAYLOAD_BUTTONS)))
|
|
&& pressed != SAFE_MODE)
|
|
loadPayload();
|
|
|
|
//If no configuration file exists or SELECT is held, load configuration menu
|
|
if(needConfig == 2 || (pressed & BUTTON_SELECT))
|
|
configureCFW(configPath);
|
|
|
|
//If screens are inited or the corresponding option is set, load splash screen
|
|
if(PDN_GPU_CNT != 1 || CONFIG(6, 1)) loadSplash();
|
|
|
|
//Determine if we need to boot an emuNAND or sysNAND
|
|
nandType = (pressed & BUTTON_L1) ? autoBootSys : ((pressed & BUTTON_R1) ? updatedSys : !autoBootSys);
|
|
|
|
/* If we're booting emuNAND the second emuNAND is set as default and B isn't pressed,
|
|
or vice-versa, boot the second emuNAND */
|
|
if(nandType && ((!(pressed & BUTTON_B)) == CONFIG(3, 1))) nandType++;
|
|
|
|
//Determine the NAND we should take the FIRM from
|
|
firmSource = (pressed & BUTTON_R1) ? !nandType : (nandType != 0);
|
|
}
|
|
}
|
|
|
|
//If we need to boot emuNAND, make sure it exists
|
|
if(nandType)
|
|
{
|
|
getEmunandSect(&emuOffset, &emuHeader, &nandType);
|
|
if(!nandType) firmSource = 0;
|
|
}
|
|
|
|
//Same if we're using emuNAND as the FIRM source
|
|
else if(firmSource)
|
|
getEmunandSect(&emuOffset, &emuHeader, &firmSource);
|
|
|
|
if(!bootType)
|
|
{
|
|
newConfig |= (nandType << 16) | (firmSource << 18);
|
|
|
|
/* If the boot configuration is different from previously, overwrite it.
|
|
Just the no-forcing flag being set is not enough */
|
|
if((newConfig & 0xEF0000) != (config & 0xFF0000))
|
|
{
|
|
//Preserve user settings (first 2 bytes)
|
|
newConfig |= config & 0xFFFF;
|
|
|
|
fileWrite(&newConfig, configPath, 3);
|
|
}
|
|
}
|
|
|
|
loadFirm(firmType, firmType == 1 && (nandType == 2 || updatedSys == !nandType));
|
|
|
|
if(firmType == 1) patchNativeFirm(firmType, nandType, emuHeader, a9lhInstalled);
|
|
else patchTwlAgbFirm(firmType);
|
|
|
|
launchFirm(bootType);
|
|
}
|
|
|
|
//Load FIRM into FCRAM
|
|
static inline void loadFirm(u32 firmType, u32 externalFirm)
|
|
{
|
|
u32 firmSize;
|
|
|
|
section = firm->section;
|
|
|
|
if(externalFirm)
|
|
{
|
|
const char *path = "/aurei/firmware.bin";
|
|
firmSize = fileSize(path);
|
|
|
|
if(firmSize)
|
|
{
|
|
fileRead(firm, path, firmSize);
|
|
|
|
//Check that the loaded FIRM matches the console
|
|
if((((u32)section[2].address >> 8) & 0xFF) != (console ? 0x60 : 0x68))
|
|
error("aurei/firmware.bin doesn't match this console,\nor it's encrypted");
|
|
}
|
|
}
|
|
else firmSize = 0;
|
|
|
|
if(!firmSize)
|
|
{
|
|
firmRead((u8 *)firm, firmFolders[firmType - 1][console]);
|
|
decryptExeFs((u8 *)firm);
|
|
}
|
|
}
|
|
|
|
static inline void patchNativeFirm(u32 firmType, u32 nandType, u32 emuHeader, u32 a9lhInstalled)
|
|
{
|
|
u8 *arm9Section = (u8 *)firm + section[2].offset;
|
|
|
|
if(console)
|
|
{
|
|
//Determine if we're booting the 9.0 FIRM
|
|
if(arm9Section[0x51] == 0xFF) firmType--;
|
|
|
|
//Decrypt ARM9Bin and patch ARM9 entrypoint to skip arm9loader
|
|
arm9Loader((u8 *)firm + section[2].offset, firmType);
|
|
firm->arm9Entry = (u8 *)0x801B01C;
|
|
}
|
|
else
|
|
{
|
|
//Determine if we're booting the 9.0 FIRM
|
|
u8 firm90Hash[0x10] = {0x27, 0x2D, 0xFE, 0xEB, 0xAF, 0x3F, 0x6B, 0x3B, 0xF5, 0xDE, 0x4C, 0x41, 0xDE, 0x95, 0x27, 0x6A};
|
|
if(memcmp(section[2].hash, firm90Hash, 0x10) == 0) firmType--;
|
|
}
|
|
|
|
if(firmType == 1 || nandType)
|
|
{
|
|
//Find the Process9 NCCH location
|
|
u8 *proc9Offset = getProc9(arm9Section, section[2].size);
|
|
|
|
//Apply emuNAND patches
|
|
if(nandType) patchEmuNAND(arm9Section, proc9Offset, emuHeader);
|
|
|
|
//Apply FIRM reboot patches, not on 9.0 FIRM as it breaks firmlaunchhax
|
|
if(firmType == 1) patchReboots(arm9Section, proc9Offset);
|
|
}
|
|
|
|
//Apply FIRM0/1 writes patches on sysNAND to protect A9LH
|
|
if(a9lhInstalled && !nandType)
|
|
{
|
|
u16 *writeOffset = getFirmWrite(arm9Section, section[2].size);
|
|
*writeOffset = writeBlock[0];
|
|
*(writeOffset + 1) = writeBlock[1];
|
|
}
|
|
|
|
//Apply signature checks patches
|
|
u32 sigOffset,
|
|
sigOffset2;
|
|
|
|
getSigChecks(arm9Section, section[2].size, &sigOffset, &sigOffset2);
|
|
*(u16 *)sigOffset = sigPatch[0];
|
|
*(u16 *)sigOffset2 = sigPatch[0];
|
|
*((u16 *)sigOffset2 + 1) = sigPatch[1];
|
|
|
|
//Replace the FIRM loader with the injector
|
|
injectLoader();
|
|
}
|
|
|
|
static inline void patchEmuNAND(u8 *arm9Section, u8 *proc9Offset, u32 emuHeader)
|
|
{
|
|
//Copy nandType code
|
|
void *emuCodeOffset = getEmuCode(proc9Offset);
|
|
memcpy(emuCodeOffset, emunand, emunand_size);
|
|
|
|
//Add the data of the found nandType
|
|
u32 *pos_offset = (u32 *)memsearch(emuCodeOffset, "NAND", emunand_size, 4);
|
|
u32 *pos_header = (u32 *)memsearch(emuCodeOffset, "NCSD", emunand_size, 4);
|
|
*pos_offset = emuOffset;
|
|
*pos_header = emuHeader;
|
|
|
|
//Find and add the SDMMC struct
|
|
u32 *pos_sdmmc = (u32 *)memsearch(emuCodeOffset, "SDMC", emunand_size, 4);
|
|
*pos_sdmmc = getSDMMC(arm9Section, section[2].size);
|
|
|
|
//Calculate offset for the hooks
|
|
u32 branchOffset = (u32)emuCodeOffset - (u32)firm -
|
|
section[2].offset + (u32)section[2].address;
|
|
|
|
//Add emunand hooks
|
|
u32 emuRead,
|
|
emuWrite;
|
|
|
|
getEmuRW(arm9Section, section[2].size, &emuRead, &emuWrite);
|
|
*(u16 *)emuRead = nandRedir[0];
|
|
*((u16 *)emuRead + 1) = nandRedir[1];
|
|
*((u32 *)emuRead + 1) = branchOffset;
|
|
*(u16 *)emuWrite = nandRedir[0];
|
|
*((u16 *)emuWrite + 1) = nandRedir[1];
|
|
*((u32 *)emuWrite + 1) = branchOffset;
|
|
|
|
//Set MPU for emu code region
|
|
u32 *mpuOffset = getMPU(arm9Section, section[2].size);
|
|
*mpuOffset = mpuPatch[0];
|
|
*(mpuOffset + 6) = mpuPatch[1];
|
|
*(mpuOffset + 9) = mpuPatch[2];
|
|
}
|
|
|
|
static inline void patchReboots(u8 *arm9Section, u8 *proc9Offset)
|
|
{
|
|
//Calculate offset for the firmlaunch code
|
|
void *rebootOffset = getReboot(arm9Section, section[2].size);
|
|
|
|
//Calculate offset for the fOpen function
|
|
u32 fOpenOffset = getfOpen(proc9Offset, rebootOffset);
|
|
|
|
//Copy firmlaunch code
|
|
memcpy(rebootOffset, reboot, reboot_size);
|
|
|
|
//Put the fOpen offset in the right location
|
|
u32 *pos_fopen = (u32 *)memsearch(rebootOffset, "OPEN", reboot_size, 4);
|
|
*pos_fopen = fOpenOffset;
|
|
}
|
|
|
|
static inline void injectLoader(void)
|
|
{
|
|
u32 loaderOffset,
|
|
loaderSize;
|
|
|
|
getLoader((u8 *)firm + section[0].offset, section[0].size, &loaderOffset, &loaderSize);
|
|
|
|
//Check that the injector CXI isn't larger than the original
|
|
if(injector_size <= (int)loaderSize)
|
|
{
|
|
memset((void *)loaderOffset, 0, loaderSize);
|
|
memcpy((void *)loaderOffset, injector, injector_size);
|
|
|
|
//Patch content size and ExeFS size to match the repaced loader's ones
|
|
*((u32 *)loaderOffset + 0x41) = loaderSize / 0x200;
|
|
*((u32 *)loaderOffset + 0x69) = loaderSize / 0x200 - 5;
|
|
}
|
|
}
|
|
|
|
static inline void patchTwlAgbFirm(u32 firmType)
|
|
{
|
|
//On N3DS, decrypt ARM9Bin and patch ARM9 entrypoint to skip arm9loader
|
|
if(console)
|
|
{
|
|
arm9Loader((u8 *)firm + section[3].offset, 0);
|
|
firm->arm9Entry = (u8 *)0x801301C;
|
|
}
|
|
|
|
static const patchData twlPatches[] = {
|
|
{{0x1650C0, 0x165D64}, {{ 6, 0x00, 0x20, 0x4E, 0xB0, 0x70, 0xBD }}, 0},
|
|
{{0x173A0E, 0x17474A}, { .type1 = 0x2001 }, 1},
|
|
{{0x174802, 0x17553E}, { .type1 = 0x2000 }, 2},
|
|
{{0x174964, 0x1756A0}, { .type1 = 0x2000 }, 2},
|
|
{{0x174D52, 0x175A8E}, { .type1 = 0x2001 }, 2},
|
|
{{0x174D5E, 0x175A9A}, { .type1 = 0x2001 }, 2},
|
|
{{0x174D6A, 0x175AA6}, { .type1 = 0x2001 }, 2},
|
|
{{0x174E56, 0x175B92}, { .type1 = 0x2001 }, 1},
|
|
{{0x174E58, 0x175B94}, { .type1 = 0x4770 }, 1}
|
|
},
|
|
agbPatches[] = {
|
|
{{0x9D2A8, 0x9DF64}, {{ 6, 0x00, 0x20, 0x4E, 0xB0, 0x70, 0xBD }}, 0},
|
|
{{0xD7A12, 0xD8B8A}, { .type1 = 0xEF26 }, 1}
|
|
};
|
|
|
|
/* Calculate the amount of patches to apply. Only count the boot screen patch for AGB_FIRM
|
|
if the matching option was enabled (keep it as last) */
|
|
u32 numPatches = firmType == 2 ? (sizeof(twlPatches) / sizeof(patchData)) : (sizeof(agbPatches) / sizeof(patchData) - !CONFIG(5, 1));
|
|
const patchData *patches = firmType == 2 ? twlPatches : agbPatches;
|
|
|
|
//Patch
|
|
for(u32 i = 0; i < numPatches; i++)
|
|
{
|
|
switch(patches[i].type)
|
|
{
|
|
case 0:
|
|
memcpy((u8 *)firm + patches[i].offset[console], patches[i].patch.type0 + 1, patches[i].patch.type0[0]);
|
|
break;
|
|
case 2:
|
|
*(u16 *)((u8 *)firm + patches[i].offset[console] + 2) = 0;
|
|
case 1:
|
|
*(u16 *)((u8 *)firm + patches[i].offset[console]) = patches[i].patch.type1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline void launchFirm(u32 bootType)
|
|
{
|
|
//Copy firm partitions to respective memory locations
|
|
for(u32 i = 0; i < 4 && section[i].size; i++)
|
|
memcpy(section[i].address, (u8 *)firm + section[i].offset, section[i].size);
|
|
|
|
//Determine the ARM11 entry to use
|
|
vu32 *arm11;
|
|
if(bootType) arm11 = (u32 *)0x1FFFFFFC;
|
|
else
|
|
{
|
|
deinitScreens();
|
|
arm11 = (u32 *)0x1FFFFFF8;
|
|
}
|
|
|
|
//Set ARM11 kernel entrypoint
|
|
*arm11 = (u32)firm->arm11Entry;
|
|
|
|
//Final jump to arm9 kernel
|
|
((void (*)())firm->arm9Entry)();
|
|
} |