This repository has been archived on 2022-05-31. You can view files and clone it, but cannot push or open issues or pull requests.
Luma3DS-3GX/source/firm.c
Aurora 7dbded99a2 First commit of the AuReiNand rewrite!
- 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.
2016-04-11 05:58:46 +02:00

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)();
}