TuxSH 5b417189db Remove "Use EmuNAND FIRM with R" option
This is a leftover from the Gateway era that has long lost its utility
and that unnecessarily complicated the code base.

Please just load an external FIRM from SD card if you need something
similar.

Also refactor the multi-EmuNAND code and CTRNAND mounting code.
2023-07-09 00:28:17 +02:00

578 lines
20 KiB
C
Executable File

/*
* This file is part of Luma3DS
* Copyright (C) 2016-2021 Aurora Wright, TuxSH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that 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/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#include "firm.h"
#include "config.h"
#include "utils.h"
#include "fs.h"
#include "exceptions.h"
#include "patches.h"
#include "memory.h"
#include "cache.h"
#include "emunand.h"
#include "crypto.h"
#include "screen.h"
#include "fmt.h"
#include "chainloader.h"
static Firm *firm = (Firm *)0x20001000;
static __attribute__((noinline)) bool overlaps(u32 as, u32 ae, u32 bs, u32 be)
{
if(as <= bs && bs <= ae)
return true;
if(bs <= as && as <= be)
return true;
return false;
}
static __attribute__((noinline)) bool inRange(u32 as, u32 ae, u32 bs, u32 be)
{
if(as >= bs && ae <= be)
return true;
return false;
}
static bool checkFirm(u32 firmSize)
{
if(memcmp(firm->magic, "FIRM", 4) != 0 || firm->arm9Entry == NULL) //Allow for the Arm11 entrypoint to be zero in which case nothing is done on the Arm11 side
return false;
bool arm9EpFound = false,
arm11EpFound = false;
u32 size = 0x200;
for(u32 i = 0; i < 4; i++)
size += firm->section[i].size;
if(firmSize < size) return false;
for(u32 i = 0; i < 4; i++)
{
FirmSection *section = &firm->section[i];
//Allow empty sections
if(section->size == 0)
continue;
if((section->offset < 0x200) ||
(section->address + section->size < section->address) || //Overflow check
((u32)section->address & 3) || (section->offset & 0x1FF) || (section->size & 0x1FF) || //Alignment check
(overlaps((u32)section->address, (u32)section->address + section->size, (u32)firm, (u32)firm + size)) ||
((!inRange((u32)section->address, (u32)section->address + section->size, 0x08000000, 0x08000000 + 0x00100000)) &&
(!inRange((u32)section->address, (u32)section->address + section->size, 0x18000000, 0x18000000 + 0x00600000)) &&
(!inRange((u32)section->address, (u32)section->address + section->size, 0x1FF00000, 0x1FFFFC00)) &&
(!inRange((u32)section->address, (u32)section->address + section->size, 0x20000000, 0x20000000 + 0x8000000))))
return false;
__attribute__((aligned(4))) u8 hash[0x20];
sha(hash, (u8 *)firm + section->offset, section->size, SHA_256_MODE);
if(memcmp(hash, section->hash, 0x20) != 0)
return false;
if(firm->arm9Entry >= section->address && firm->arm9Entry < (section->address + section->size))
arm9EpFound = true;
if(firm->arm11Entry >= section->address && firm->arm11Entry < (section->address + section->size))
arm11EpFound = true;
}
return arm9EpFound && (firm->arm11Entry == NULL || arm11EpFound);
}
static inline u32 loadFirmFromStorage(FirmwareType firmType)
{
static const char *firmwareFiles[] = {
"native.firm",
"twl.firm",
"agb.firm",
"safe.firm",
"sysupdater.firm"
},
*cetkFiles[] = {
"cetk",
"cetk_twl",
"cetk_agb",
"cetk_safe",
"cetk_sysupdater"
};
u32 firmSize = fileRead(firm, firmwareFiles[(u32)firmType], 0x400000 + sizeof(Cxi) + 0x200);
if(!firmSize) return 0;
static const char *extFirmError = "The external FIRM is not valid.";
if(firmSize <= sizeof(Cxi) + 0x200) error(extFirmError);
if(memcmp(firm, "FIRM", 4) != 0)
{
if(firmSize <= sizeof(Cxi) + 0x400) error(extFirmError);
u8 cetk[0xA50];
if(fileRead(cetk, cetkFiles[(u32)firmType], sizeof(cetk)) != sizeof(cetk))
error("The cetk is missing or corrupted.");
firmSize = decryptNusFirm((Ticket *)(cetk + 0x140), (Cxi *)firm, firmSize);
if(!firmSize) error("Unable to decrypt the external FIRM.");
}
if(!checkFirm(firmSize)) error("The external FIRM is invalid or corrupted.");
return firmSize;
}
u32 loadNintendoFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadFromStorage, bool isSafeMode)
{
u32 firmVersion = 0xFFFFFFFF,
firmSize;
bool ctrNandError = isSdMode && !remountCtrNandPartition(false);
if(!ctrNandError)
{
//Load FIRM from CTRNAND
firmVersion = firmRead(firm, (u32)*firmType);
if(firmVersion == 0xFFFFFFFF) ctrNandError = true;
else
{
firmSize = decryptExeFs((Cxi *)firm);
if(!firmSize || !checkFirm(firmSize)) ctrNandError = true;
}
}
bool loadedFromStorage = false;
if(loadFromStorage || ctrNandError)
{
u32 result = loadFirmFromStorage(*firmType);
if(result != 0)
{
loadedFromStorage = true;
firmSize = result;
}
else if(ctrNandError) error("Unable to mount CTRNAND or load the CTRNAND FIRM.\nPlease use an external one.");
}
//Check that the FIRM is right for the console from the Arm9 section address
if((firm->section[3].offset != 0 ? firm->section[3].address : firm->section[2].address) != (ISN3DS ? (u8 *)0x8006000 : (u8 *)0x8006800))
error("The %s FIRM is not for this console.", loadedFromStorage ? "external" : "CTRNAND");
if(!ISN3DS && *firmType == NATIVE_FIRM && firm->section[0].address == (u8 *)0x1FF80000)
{
//We can't boot < 3.x EmuNANDs
if(nandType != FIRMWARE_SYSNAND) error("An old unsupported EmuNAND has been detected.\nLuma3DS is unable to boot it.");
//If you want to use SAFE_FIRM on 1.0, use Luma from NAND & comment this line:
if(isSafeMode) error("SAFE_MODE is not supported on 1.x/2.x FIRM.");
*firmType = NATIVE_FIRM1X2X;
}
if(loadedFromStorage || ISDEVUNIT)
{
firmVersion = 0xFFFFFFFF;
if(!ISN3DS && *firmType == NATIVE_FIRM)
{
__attribute__((aligned(4))) static const u8 hashes[3][0x20] = {
{0x39, 0x75, 0xB5, 0x28, 0x24, 0x5E, 0x8B, 0x56, 0xBC, 0x83, 0x79, 0x41, 0x09, 0x2C, 0x42, 0xE6,
0x26, 0xB6, 0x80, 0x59, 0xA5, 0x56, 0xF9, 0xF9, 0x6E, 0xF3, 0x63, 0x05, 0x58, 0xDF, 0x35, 0xEF},
{0x81, 0x9E, 0x71, 0x58, 0xE5, 0x44, 0x73, 0xF7, 0x48, 0x78, 0x7C, 0xEF, 0x5E, 0x30, 0xE2, 0x28,
0x78, 0x0B, 0x21, 0x23, 0x94, 0x63, 0xE8, 0x4E, 0x06, 0xBB, 0xD6, 0x8D, 0xA0, 0x99, 0xAE, 0x98},
{0x1D, 0xD5, 0xB0, 0xC2, 0xD9, 0x4A, 0x4A, 0xF3, 0x23, 0xDD, 0x2F, 0x65, 0x21, 0x95, 0x9B, 0x7E,
0xF2, 0x71, 0x7E, 0xB6, 0x7A, 0x3A, 0x74, 0x78, 0x0D, 0xE3, 0xB5, 0x0C, 0x2B, 0x7F, 0x85, 0x37}
};
u32 i;
for(i = 0; i < 3; i++) if(memcmp(firm->section[1].hash, hashes[i], 0x20) == 0) break;
switch(i)
{
case 0:
firmVersion = 0x18;
break;
case 1:
firmVersion = 0x1D;
break;
case 2:
firmVersion = 0x1F;
break;
default:
break;
}
}
}
return firmVersion;
}
void loadHomebrewFirm(u32 pressed)
{
char path[10 + 255];
bool hasDisplayedMenu = false;
bool found = !pressed ? payloadMenu(path, &hasDisplayedMenu) : findPayload(path, pressed);
if(!found) return;
u32 maxPayloadSize = (u32)((u8 *)0x27FFE000 - (u8 *)firm),
payloadSize = fileRead(firm, path, maxPayloadSize);
if(payloadSize <= 0x200 || !checkFirm(payloadSize)) error("The payload is invalid or corrupted.");
char absPath[24 + 255];
if(isSdMode) sprintf(absPath, "sdmc:/luma/%s", path);
else sprintf(absPath, "nand:/rw/luma/%s", path);
char *argv[2] = {absPath, (char *)fbs};
bool wantsScreenInit = (firm->reserved2[0] & 1) != 0;
if(!hasDisplayedMenu && wantsScreenInit)
initScreens(); // Don't init the screens unless we have to, if not already done
launchFirm(wantsScreenInit ? 2 : 1, argv);
}
static inline void mergeSection0(FirmwareType firmType, u32 firmVersion, bool loadFromStorage)
{
u32 srcModuleSize,
nbModules = 0;
struct
{
char name[8];
u8 *src;
u32 size;
} moduleList[6];
//1) Parse info concerning Nintendo's modules
for(u8 *src = (u8 *)firm + firm->section[0].offset, *srcEnd = src + firm->section[0].size; src < srcEnd; src += srcModuleSize, nbModules++)
{
memcpy(moduleList[nbModules].name, ((Cxi *)src)->exHeader.systemControlInfo.appTitle, 8);
moduleList[nbModules].src = src;
srcModuleSize = moduleList[nbModules].size = ((Cxi *)src)->ncch.contentSize * 0x200;
}
// SAFE_FIRM only for N3DS and only if ENABLESAFEFIRMROSALINA is on
if((firmType == NATIVE_FIRM || firmType == SAFE_FIRM) && (ISN3DS || firmVersion >= 0x1D))
{
//2) Merge that info with our own modules'
for(u8 *src = (u8 *)0x18180000; memcmp(((Cxi *)src)->ncch.magic, "NCCH", 4) == 0; src += srcModuleSize)
{
const char *name = ((Cxi *)src)->exHeader.systemControlInfo.appTitle;
u32 i;
for(i = 0; i < 5 && memcmp(name, moduleList[i].name, 8) != 0; i++);
if(i == 5)
{
nbModules++;
memcpy(moduleList[i].name, ((Cxi *)src)->exHeader.systemControlInfo.appTitle, 8);
}
moduleList[i].src = src;
srcModuleSize = moduleList[i].size = ((Cxi *)src)->ncch.contentSize * 0x200;
}
}
//3) Read or copy the modules
u8 *dst = firm->section[0].address;
const char *extModuleSizeError = "The external FIRM modules are too large.";
// SAFE_FIRM only for N3DS and only if ENABLESAFEFIRMROSALINA is on
u32 maxModuleSize = (firmType == NATIVE_FIRM || firmType == SAFE_FIRM) ? 0x80000 : 0x600000;
for(u32 i = 0, dstModuleSize; i < nbModules; i++, dst += dstModuleSize, maxModuleSize -= dstModuleSize)
{
if(loadFromStorage)
{
char fileName[24];
//Read modules from files if they exist
sprintf(fileName, "sysmodules/%.8s.cxi", moduleList[i].name);
dstModuleSize = getFileSize(fileName);
if(dstModuleSize != 0)
{
if(dstModuleSize > maxModuleSize) error(extModuleSizeError);
if(dstModuleSize <= sizeof(Cxi) + 0x200 ||
fileRead(dst, fileName, dstModuleSize) != dstModuleSize ||
memcmp(((Cxi *)dst)->ncch.magic, "NCCH", 4) != 0 ||
memcmp(moduleList[i].name, ((Cxi *)dst)->exHeader.systemControlInfo.appTitle, sizeof(((Cxi *)dst)->exHeader.systemControlInfo.appTitle)) != 0)
error("An external FIRM module is invalid or corrupted.");
continue;
}
}
dstModuleSize = moduleList[i].size;
if(dstModuleSize > maxModuleSize) error(extModuleSizeError);
memcpy(dst, moduleList[i].src, dstModuleSize);
}
//4) Patch NATIVE_FIRM/SAFE_FIRM (N3DS) if necessary
if(nbModules == 6)
{
if(patchK11ModuleLoading(firm->section[0].size, dst - firm->section[0].address, (u8 *)firm + firm->section[1].offset, firm->section[1].size) != 0)
error("Failed to inject custom sysmodule");
}
}
u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, bool loadFromStorage, bool isFirmProtEnabled, bool needToInitSd, bool doUnitinfoPatch)
{
u8 *arm9Section = (u8 *)firm + firm->section[2].offset;
if(ISN3DS)
{
//Decrypt Arm9Bin and patch Arm9 entrypoint to skip kernel9loader
kernel9Loader((Arm9Bin *)arm9Section);
firm->arm9Entry = (u8 *)0x801B01C;
}
//Find the Process9 .code location, size and memory address
u32 process9Size,
process9MemAddr;
u8 *process9Offset = getProcess9Info(arm9Section, firm->section[2].size, &process9Size, &process9MemAddr);
u32 kernel9Size = (u32)(process9Offset - arm9Section) - sizeof(Cxi) - 0x200,
ret = 0;
#ifndef BUILD_FOR_EXPLOIT_DEV
//Skip on FIRMs < 4.0
if(ISN3DS || firmVersion >= 0x1D)
{
//Find the Kernel11 SVC table and handler, exceptions page and free space locations
u8 *arm11Section1 = (u8 *)firm + firm->section[1].offset;
u32 baseK11VA;
u8 *freeK11Space;
u32 *arm11SvcHandler,
*arm11ExceptionsPage,
*arm11SvcTable = getKernel11Info(arm11Section1, firm->section[1].size, &baseK11VA, &freeK11Space, &arm11SvcHandler, &arm11ExceptionsPage);
ret += installK11Extension(arm11Section1, firm->section[1].size, needToInitSd, baseK11VA, arm11ExceptionsPage, &freeK11Space);
ret += patchKernel11(arm11Section1, firm->section[1].size, baseK11VA, arm11SvcTable, arm11ExceptionsPage);
}
#else
(void)needToInitSd;
#endif
//Apply signature patches
ret += patchSignatureChecks(process9Offset, process9Size);
//Apply EmuNAND patches
if(nandType != FIRMWARE_SYSNAND) ret += patchEmuNand(arm9Section, kernel9Size, process9Offset, process9Size, firm->section[2].address, firmVersion);
//Apply FIRM0/1 writes patches on SysNAND to protect A9LH
else if(isFirmProtEnabled) ret += patchFirmWrites(process9Offset, process9Size);
#ifndef BUILD_FOR_EXPLOIT_DEV
//Apply firmlaunch patches
ret += patchFirmlaunches(process9Offset, process9Size, process9MemAddr);
#endif
//Apply dev unit check patches related to NCCH encryption
if(!ISDEVUNIT)
{
ret += patchZeroKeyNcchEncryptionCheck(process9Offset, process9Size);
ret += patchNandNcchEncryptionCheck(process9Offset, process9Size);
}
//Apply anti-anti-DG patches on 11.0+
if(firmVersion >= (ISN3DS ? 0x21 : 0x52)) ret += patchTitleInstallMinVersionChecks(process9Offset, process9Size, firmVersion);
//Patch P9 AM ticket wrapper on 11.8+ to use 0 Key and IV, only with UNITINFO patch on to prevent NIM from actually sending any
if(doUnitinfoPatch && firmVersion >= (ISN3DS ? 0x35 : 0x64)) ret += patchP9AMTicketWrapperZeroKeyIV(process9Offset, process9Size, firmVersion);
//Apply UNITINFO patches
if(doUnitinfoPatch)
{
ret += patchUnitInfoValueSet(arm9Section, kernel9Size);
if(!ISDEVUNIT) ret += patchCheckForDevCommonKey(process9Offset, process9Size);
}
//Arm9 exception handlers
ret += patchArm9ExceptionHandlersInstall(arm9Section, kernel9Size);
ret += patchSvcBreak9(arm9Section, kernel9Size, (u32)firm->section[2].address);
ret += patchKernel9Panic(arm9Section, kernel9Size);
ret += patchP9AccessChecks(process9Offset, process9Size);
mergeSection0(NATIVE_FIRM, firmVersion, loadFromStorage);
firm->section[0].size = 0;
return ret;
}
u32 patchTwlFirm(u32 firmVersion, bool loadFromStorage, bool doUnitinfoPatch)
{
u8 *arm9Section = (u8 *)firm + firm->section[3].offset;
// Below 3.0, do not actually do anything.
if(!ISN3DS && firmVersion < 0xC)
return 0;
//On N3DS, decrypt Arm9Bin and patch Arm9 entrypoint to skip kernel9loader
if(ISN3DS)
{
kernel9Loader((Arm9Bin *)arm9Section);
firm->arm9Entry = (u8 *)0x801301C;
}
//Find the Process9 .code location, size and memory address
u32 process9Size,
process9MemAddr;
u8 *process9Offset = getProcess9Info(arm9Section, firm->section[3].size, &process9Size, &process9MemAddr);
u32 kernel9Size = (u32)(process9Offset - arm9Section) - sizeof(Cxi) - 0x200,
ret = 0;
ret += patchLgySignatureChecks(process9Offset, process9Size);
ret += patchTwlInvalidSignatureChecks(process9Offset, process9Size);
ret += patchTwlNintendoLogoChecks(process9Offset, process9Size);
ret += patchTwlWhitelistChecks(process9Offset, process9Size);
if(ISN3DS || firmVersion > 0x11) ret += patchTwlFlashcartChecks(process9Offset, process9Size, firmVersion);
else if(!ISN3DS && firmVersion == 0x11) ret += patchOldTwlFlashcartChecks(process9Offset, process9Size);
ret += patchTwlShaHashChecks(process9Offset, process9Size);
//Apply UNITINFO patch
if(doUnitinfoPatch) ret += patchUnitInfoValueSet(arm9Section, kernel9Size);
if(loadFromStorage)
{
mergeSection0(TWL_FIRM, 0, true);
firm->section[0].size = 0;
}
return ret;
}
u32 patchAgbFirm(bool loadFromStorage, bool doUnitinfoPatch)
{
u8 *arm9Section = (u8 *)firm + firm->section[3].offset;
//On N3DS, decrypt Arm9Bin and patch Arm9 entrypoint to skip kernel9loader
if(ISN3DS)
{
kernel9Loader((Arm9Bin *)arm9Section);
firm->arm9Entry = (u8 *)0x801301C;
}
//Find the Process9 .code location, size and memory address
u32 process9Size,
process9MemAddr;
u8 *process9Offset = getProcess9Info(arm9Section, firm->section[3].size, &process9Size, &process9MemAddr);
u32 kernel9Size = (u32)(process9Offset - arm9Section) - sizeof(Cxi) - 0x200,
ret = 0;
ret += patchLgySignatureChecks(process9Offset, process9Size);
if(CONFIG(SHOWGBABOOT)) ret += patchAgbBootSplash(process9Offset, process9Size);
//Apply UNITINFO patch
if(doUnitinfoPatch) ret += patchUnitInfoValueSet(arm9Section, kernel9Size);
if(loadFromStorage)
{
mergeSection0(AGB_FIRM, 0, true);
firm->section[0].size = 0;
}
return ret;
}
u32 patch1x2xNativeAndSafeFirm(void)
{
u8 *arm9Section = (u8 *)firm + firm->section[2].offset;
if(ISN3DS)
{
//Decrypt Arm9Bin and patch Arm9 entrypoint to skip kernel9loader
kernel9Loader((Arm9Bin *)arm9Section);
firm->arm9Entry = (u8 *)0x801B01C;
}
//Find the Process9 .code location, size and memory address
u32 process9Size,
process9MemAddr;
u8 *process9Offset = getProcess9Info(arm9Section, firm->section[2].size, &process9Size, &process9MemAddr);
u32 kernel9Size = (u32)(process9Offset - arm9Section) - sizeof(Cxi) - 0x200,
ret = 0;
ret += ISN3DS ? patchFirmWrites(process9Offset, process9Size) : patchOldFirmWrites(process9Offset, process9Size);
ret += ISN3DS ? patchSignatureChecks(process9Offset, process9Size) : patchOldSignatureChecks(process9Offset, process9Size);
//Arm9 exception handlers
ret += patchArm9ExceptionHandlersInstall(arm9Section, kernel9Size);
ret += patchSvcBreak9(arm9Section, kernel9Size, (u32)firm->section[2].address);
//Apply firmlaunch patches
//Doesn't work here if Luma is on SD. If you want to use SAFE_FIRM on 1.0, use Luma from NAND & uncomment this line:
//ret += patchFirmlaunches(process9Offset, process9Size, process9MemAddr);
if(ISN3DS && CONFIG(ENABLESAFEFIRMROSALINA))
{
u8 *arm11Section1 = (u8 *)firm + firm->section[1].offset;
//Find the Kernel11 SVC table and handler, exceptions page and free space locations
u32 baseK11VA;
u8 *freeK11Space;
u32 *arm11SvcHandler,
*arm11ExceptionsPage,
*arm11SvcTable = getKernel11Info(arm11Section1, firm->section[1].size, &baseK11VA, &freeK11Space, &arm11SvcHandler, &arm11ExceptionsPage);
ret += installK11Extension(arm11Section1, firm->section[1].size, false, baseK11VA, arm11ExceptionsPage, &freeK11Space);
ret += patchKernel11(arm11Section1, firm->section[1].size, baseK11VA, arm11SvcTable, arm11ExceptionsPage);
// Add some other patches to the mix, as we can now launch homebrew on SAFE_FIRM:
ret += patchKernel9Panic(arm9Section, kernel9Size);
ret += patchP9AccessChecks(process9Offset, process9Size);
mergeSection0(NATIVE_FIRM, 0x45, false); // may change in the future
firm->section[0].size = 0;
}
return ret;
}
void launchFirm(int argc, char **argv)
{
prepareArm11ForFirmlaunch();
chainload(argc, argv, firm);
}