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

398 lines
14 KiB
C
Raw Normal View History

2015-08-05 03:57:37 +02:00
/*
2016-07-05 16:05:53 +02:00
* This file is part of Luma3DS
* Copyright (C) 2016 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 of GPLv3 applies 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.
2015-08-05 03:57:37 +02:00
*/
#include "firm.h"
2016-04-05 05:27:28 +02:00
#include "config.h"
#include "utils.h"
#include "fs.h"
2016-09-19 14:03:55 +02:00
#include "exceptions.h"
2015-08-05 03:57:37 +02:00
#include "patches.h"
#include "memory.h"
#include "strings.h"
#include "cache.h"
2015-08-05 03:57:37 +02:00
#include "emunand.h"
#include "crypto.h"
#include "screen.h"
2016-09-23 02:06:04 +02:00
#include "../build/bundled.h"
2016-10-10 03:39:23 +02:00
static Firm *firm = (Firm *)0x24000000;
static inline bool loadFirmFromStorage(FirmwareType firmType)
{
const char *firmwareFiles[] = {
2016-10-12 02:28:08 +02:00
"firmware.bin",
"firmware_twl.bin",
"firmware_agb.bin",
2016-11-12 22:27:03 +01:00
"firmware_safe.bin",
"firmware_sysupdater.bin"
},
*cetkFiles[] = {
2016-10-12 02:28:08 +02:00
"cetk",
"cetk_twl",
"cetk_agb",
2016-11-12 22:27:03 +01:00
"cetk_safe",
"cetk_sysupdater"
};
2016-04-04 18:19:00 +02:00
u32 firmSize = fileRead(firm, firmType == NATIVE_FIRM1X2X ? firmwareFiles[0] : firmwareFiles[(u32)firmType], 0x400000 + sizeof(Cxi) + 0x200);
if(!firmSize) return false;
if(firmSize <= sizeof(Cxi) + 0x200) error("The FIRM in /luma is not valid.");
if(memcmp(firm, "FIRM", 4) != 0)
{
u8 cetk[0xA50];
if(fileRead(cetk, firmType == NATIVE_FIRM1X2X ? cetkFiles[0] : cetkFiles[(u32)firmType], sizeof(cetk)) != sizeof(cetk) ||
!decryptNusFirm((Ticket *)(cetk + 0x140), (Cxi *)firm, firmSize))
error("The FIRM in /luma is encrypted or corrupted.");
}
//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 FIRM in /luma is not for this console.");
return true;
}
u32 loadFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadFromStorage, bool isSafeMode)
{
2016-09-08 22:33:25 +02:00
//Load FIRM from CTRNAND
u32 firmVersion = firmRead(firm, (u32)*firmType);
2016-09-08 22:33:25 +02:00
2016-10-10 18:56:19 +02:00
if(firmVersion == 0xFFFFFFFF) error("Failed to get the CTRNAND FIRM.");
2016-10-08 01:52:04 +02:00
bool mustLoadFromStorage = false;
2016-04-28 16:11:25 +02:00
2016-11-05 17:46:47 +01:00
if(!ISN3DS && *firmType == NATIVE_FIRM && !ISDEVUNIT)
{
if(firmVersion < 0x18)
2016-08-27 13:11:55 +02:00
{
2016-09-02 15:15:22 +02:00
//We can't boot < 3.x EmuNANDs
2016-12-22 23:11:15 +01:00
if(nandType != FIRMWARE_SYSNAND)
error("An old unsupported EmuNAND has been detected.\nLuma3DS is unable to boot it.");
2016-08-28 02:38:30 +02:00
if(isSafeMode) error("SAFE_MODE is not supported on 1.x/2.x FIRM.");
*firmType = NATIVE_FIRM1X2X;
2016-08-27 13:11:55 +02:00
}
2016-10-08 01:52:04 +02:00
//We can't boot a 3.x/4.x NATIVE_FIRM, load one from SD/CTRNAND
else if(firmVersion < 0x25) mustLoadFromStorage = true;
}
if((loadFromStorage || mustLoadFromStorage) && loadFirmFromStorage(*firmType)) firmVersion = 0xFFFFFFFF;
else
{
2016-10-08 01:52:04 +02:00
if(mustLoadFromStorage) error("An old unsupported FIRM has been detected.\nCopy a firmware.bin in /luma to boot.");
if(!decryptExeFs((Cxi *)firm)) error("The CTRNAND FIRM is corrupted.");
if(ISDEVUNIT) firmVersion = 0xFFFFFFFF;
}
2016-09-08 22:33:25 +02:00
return firmVersion;
2015-08-05 11:54:00 +02:00
}
u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, bool isA9lhInstalled, bool isSafeMode, u32 devMode)
{
u8 *arm9Section = (u8 *)firm + firm->section[2].offset,
*arm11Section1 = (u8 *)firm + firm->section[1].offset;
2016-05-27 14:08:06 +02:00
if(ISN3DS)
{
2016-09-20 14:29:26 +02:00
//Decrypt ARM9Bin and patch ARM9 entrypoint to skip kernel9loader
2016-10-08 14:23:08 +02:00
kernel9Loader((Arm9Bin *)arm9Section);
firm->arm9Entry = (u8 *)0x801B01C;
}
2016-11-04 22:31:43 +01:00
//Sets the 7.x NCCH KeyX and the 6.x gamecard save data KeyY on >= 6.0 O3DS FIRMs, if not using A9LH
else if(!ISA9LH && !ISFIRMLAUNCH && firmVersion >= 0x29) set6x7xKeys();
//Find the Process9 .code location, size and memory address
u32 process9Size,
process9MemAddr;
2016-10-10 13:29:34 +02:00
u8 *process9Offset = getProcess9Info(arm9Section, firm->section[2].size, &process9Size, &process9MemAddr);
//Find the Kernel11 SVC table and handler, exceptions page and free space locations
2016-09-10 21:12:53 +02:00
u32 baseK11VA;
2016-08-29 15:35:24 +02:00
u8 *freeK11Space;
u32 *arm11SvcHandler,
2016-12-22 23:11:15 +01:00
*arm11DAbtHandler,
2016-09-03 15:41:45 +02:00
*arm11ExceptionsPage,
2016-12-22 23:11:15 +01:00
*arm11SvcTable = getKernel11Info(arm11Section1, firm->section[1].size, &baseK11VA, &freeK11Space, &arm11SvcHandler, &arm11DAbtHandler, &arm11ExceptionsPage);
2016-10-15 18:05:46 +02:00
u32 kernel9Size = (u32)(process9Offset - arm9Section) - sizeof(Cxi) - 0x200,
ret = 0;
2016-08-29 15:35:24 +02:00
2016-05-25 20:08:37 +02:00
//Apply signature patches
ret += patchSignatureChecks(process9Offset, process9Size);
2016-05-27 14:08:06 +02:00
2016-09-08 02:12:29 +02:00
//Apply EmuNAND patches
if(nandType != FIRMWARE_SYSNAND) ret += patchEmuNand(arm9Section, kernel9Size, process9Offset, process9Size, emuHeader, firm->section[2].address);
//Apply FIRM0/1 writes patches on SysNAND to protect A9LH
else if(isA9lhInstalled) ret += patchFirmWrites(process9Offset, process9Size);
2016-07-19 20:03:35 +02:00
//Apply firmlaunch patches
ret += patchFirmlaunches(process9Offset, process9Size, process9MemAddr);
2016-11-10 13:17:01 +01:00
//Apply dev unit check patches related to NCCH encryption
if(!ISDEVUNIT)
{
ret += patchZeroKeyNcchEncryptionCheck(process9Offset, process9Size);
ret += patchNandNcchEncryptionCheck(process9Offset, process9Size);
}
//11.0 FIRM patches
if(firmVersion >= (ISN3DS ? 0x21 : 0x52))
2016-05-27 04:16:49 +02:00
{
//Apply anti-anti-DG patches
ret += patchTitleInstallMinVersionChecks(process9Offset, process9Size, firmVersion);
2016-05-27 04:16:49 +02:00
//Restore svcBackdoor
2016-10-10 03:05:23 +02:00
ret += reimplementSvcBackdoor(arm11Section1, arm11SvcTable, baseK11VA, &freeK11Space);
2016-05-27 14:50:30 +02:00
}
ret += implementSvcGetCFWInfo(arm11Section1, arm11SvcTable, baseK11VA, &freeK11Space, isSafeMode);
2016-11-10 13:17:01 +01:00
//Apply UNITINFO patches
if(devMode == 2)
{
ret += patchUnitInfoValueSet(arm9Section, kernel9Size);
2016-11-10 13:17:01 +01:00
if(!ISDEVUNIT) ret += patchCheckForDevCommonKey(process9Offset, process9Size);
}
if(devMode != 0 && isA9lhInstalled)
{
2016-09-19 14:03:55 +02:00
//ARM11 exception handlers
u32 codeSetOffset,
stackAddress = getInfoForArm11ExceptionHandlers(arm11Section1, firm->section[1].size, &codeSetOffset);
2016-12-22 23:11:15 +01:00
ret += installArm11Handlers(arm11ExceptionsPage, stackAddress, codeSetOffset, arm11DAbtHandler, baseK11VA + ((u8 *)arm11DAbtHandler - arm11Section1));
2016-09-19 14:03:55 +02:00
patchSvcBreak11(arm11Section1, arm11SvcTable);
ret += patchKernel11Panic(arm11Section1, firm->section[1].size);
2016-09-19 14:03:55 +02:00
//ARM9 exception handlers
ret += patchArm9ExceptionHandlersInstall(arm9Section, kernel9Size);
ret += patchSvcBreak9(arm9Section, kernel9Size, (u32)firm->section[2].address);
ret += patchKernel9Panic(arm9Section, kernel9Size);
}
bool patchAccess = CONFIG(PATCHACCESS),
patchGames = CONFIG(PATCHGAMES);
if(patchAccess || patchGames)
{
ret += patchK11ModuleChecks(arm11Section1, firm->section[1].size, &freeK11Space, patchGames);
if(patchAccess)
{
ret += patchArm11SvcAccessChecks(arm11SvcHandler, (u32 *)(arm11Section1 + firm->section[1].size));
ret += patchP9AccessChecks(process9Offset, process9Size);
}
}
return ret;
}
u32 patchTwlFirm(u32 firmVersion, u32 devMode)
{
u8 *arm9Section = (u8 *)firm + firm->section[3].offset;
2016-12-22 23:11:15 +01:00
//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);
2016-10-10 02:51:44 +02:00
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(devMode == 2) ret += patchUnitInfoValueSet(arm9Section, kernel9Size);
return ret;
}
u32 patchAgbFirm(u32 devMode)
{
u8 *arm9Section = (u8 *)firm + firm->section[3].offset;
2016-12-22 23:11:15 +01:00
2016-09-20 14:29:26 +02:00
//On N3DS, decrypt ARM9Bin and patch ARM9 entrypoint to skip kernel9loader
if(ISN3DS)
{
2016-10-08 14:23:08 +02:00
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;
2016-10-10 02:51:44 +02:00
ret += patchLgySignatureChecks(process9Offset, process9Size);
if(CONFIG(SHOWGBABOOT)) ret += patchAgbBootSplash(process9Offset, process9Size);
2016-09-08 18:47:13 +02:00
//Apply UNITINFO patch
if(devMode == 2) ret += patchUnitInfoValueSet(arm9Section, kernel9Size);
return ret;
}
u32 patch1x2xNativeAndSafeFirm(u32 devMode)
{
u8 *arm9Section = (u8 *)firm + firm->section[2].offset;
if(ISN3DS)
{
2016-09-20 14:29:26 +02:00
//Decrypt ARM9Bin and patch ARM9 entrypoint to skip kernel9loader
2016-10-08 14:23:08 +02:00
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);
2016-09-19 14:57:36 +02:00
if(devMode != 0)
{
2016-09-19 14:03:55 +02:00
//ARM9 exception handlers
ret += patchArm9ExceptionHandlersInstall(arm9Section, kernel9Size);
ret += patchSvcBreak9(arm9Section, kernel9Size, (u32)firm->section[2].address);
}
return ret;
}
2016-10-08 01:52:04 +02:00
static inline void copySection0AndInjectSystemModules(FirmwareType firmType, bool loadFromStorage)
{
u32 maxModuleSize = firmType == NATIVE_FIRM ? 0x80000 : 0x600000,
srcModuleSize,
dstModuleSize;
2016-10-16 04:03:00 +02:00
const char *extModuleSizeError = "The external FIRM modules are too large.";
2016-05-12 14:48:52 +02:00
for(u8 *src = (u8 *)firm + firm->section[0].offset, *srcEnd = src + firm->section[0].size, *dst = firm->section[0].address;
src < srcEnd; src += srcModuleSize, dst += dstModuleSize, maxModuleSize -= dstModuleSize)
{
srcModuleSize = ((Cxi *)src)->ncch.contentSize * 0x200;
const char *moduleName = ((Cxi *)src)->exHeader.systemControlInfo.appTitle;
if(loadFromStorage)
2016-09-19 14:03:55 +02:00
{
2016-10-12 02:28:08 +02:00
char fileName[24] = "sysmodules/";
2016-09-19 14:03:55 +02:00
//Read modules from files if they exist
concatenateStrings(fileName, moduleName);
2016-11-02 15:36:20 +01:00
concatenateStrings(fileName, ".cxi");
2016-09-19 14:03:55 +02:00
dstModuleSize = getFileSize(fileName);
if(dstModuleSize != 0)
{
2016-10-16 04:03:00 +02:00
if(dstModuleSize > maxModuleSize) error(extModuleSizeError);
if(dstModuleSize <= sizeof(Cxi) + 0x200 ||
fileRead(dst, fileName, dstModuleSize) != dstModuleSize ||
memcmp(((Cxi *)dst)->ncch.magic, "NCCH", 4) != 0 ||
memcmp(moduleName, ((Cxi *)dst)->exHeader.systemControlInfo.appTitle, sizeof(((Cxi *)dst)->exHeader.systemControlInfo.appTitle)) != 0)
2016-10-16 23:34:26 +02:00
error("An external FIRM module is invalid or corrupted.");
continue;
}
2016-09-19 14:03:55 +02:00
}
const u8 *module;
if(firmType == NATIVE_FIRM && memcmp(moduleName, "loader", 6) == 0)
{
module = injector_bin;
dstModuleSize = injector_bin_size;
}
else
{
module = src;
dstModuleSize = srcModuleSize;
}
if(dstModuleSize > maxModuleSize) error(extModuleSizeError);
memcpy(dst, module, dstModuleSize);
}
}
2016-09-08 18:47:13 +02:00
void launchFirm(FirmwareType firmType, bool loadFromStorage)
{
//Allow module injection and/or inject 3ds_injector on new NATIVE_FIRMs and LGY FIRMs
u32 sectionNum;
2016-11-29 19:51:07 +01:00
if(firmType == NATIVE_FIRM || (loadFromStorage && (firmType == TWL_FIRM || firmType == AGB_FIRM)))
{
2016-10-08 01:52:04 +02:00
copySection0AndInjectSystemModules(firmType, loadFromStorage);
sectionNum = 1;
}
else sectionNum = 0;
2015-08-05 03:57:37 +02:00
2016-04-26 20:06:31 +02:00
//Copy FIRM sections to respective memory locations
for(; sectionNum < 4 && firm->section[sectionNum].size != 0; sectionNum++)
memcpy(firm->section[sectionNum].address, (u8 *)firm + firm->section[sectionNum].offset, firm->section[sectionNum].size);
2016-02-29 16:28:43 +01:00
//Determine the ARM11 entry to use
vu32 *arm11;
if(ISFIRMLAUNCH) arm11 = (vu32 *)0x1FFFFFFC;
else
{
deinitScreens();
arm11 = (vu32 *)BRAHMA_ARM11_ENTRY;
}
//Set ARM11 kernel entrypoint
*arm11 = (u32)firm->arm11Entry;
2016-09-15 19:53:51 +02:00
//Ensure that all memory transfers have completed and that the caches have been flushed
flushEntireDCache();
flushEntireICache();
2016-04-24 19:23:40 +02:00
//Final jump to ARM9 kernel
((void (*)())firm->arm9Entry)();
2016-12-22 23:11:15 +01:00
}