
- Move emuNAND hook to ITCM, fixing previously untested emuNAND support for 5.0 (and possibly more versions). This was a very long-standing bug - Fix booting on versions 4.x to 8.x, and restore full support for 5.x to 8.x. All Arm11 custom sysmodules and k11ext have been disabled on 4.x because the Luma3DS v13.0 changes couldn't be ported (this means no Rosalina and no region-free and such, and while I could restore some of the functionality, you should just update *after* installing Luma, like you've been instructed to)
187 lines
5.5 KiB
C
187 lines
5.5 KiB
C
/*
|
|
* 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.
|
|
*/
|
|
|
|
/*
|
|
* Code for locating the SDMMC struct by Normmatt
|
|
*/
|
|
|
|
|
|
#include "emunand.h"
|
|
#include "memory.h"
|
|
#include "utils.h"
|
|
#include "fatfs/sdmmc/sdmmc.h"
|
|
#include "large_patches.h"
|
|
|
|
u32 emuOffset,
|
|
emuHeader;
|
|
|
|
void locateEmuNand(FirmwareSource *nandType, u32 *emunandIndex, bool configureCtrNandParams)
|
|
{
|
|
static u8 __attribute__((aligned(4))) temp[0x200];
|
|
static u32 nandSize = 0,
|
|
fatStart;
|
|
|
|
if(!nandSize)
|
|
{
|
|
nandSize = getMMCDevice(0)->total_size;
|
|
sdmmc_sdcard_readsectors(0, 1, temp);
|
|
fatStart = *(u32 *)(temp + 0x1C6); //First sector of the FAT partition
|
|
}
|
|
|
|
/*if (*nandType == FIRMWARE_SYSNAND)
|
|
return;*/
|
|
|
|
for(u32 i = 0; i < 3; i++) // Test the different kinds of multi-EmuNAND there are, unless we are looking for the first one
|
|
{
|
|
static const u32 roundedMinsizes[] = {0x1D8000, 0x26E000};
|
|
|
|
u32 nandOffset;
|
|
switch(i)
|
|
{
|
|
case 1:
|
|
nandOffset = ROUND_TO_4MB(nandSize + 1); //"Default" layout
|
|
break;
|
|
case 2:
|
|
nandOffset = roundedMinsizes[ISN3DS ? 1 : 0]; //"Minsize" layout
|
|
break;
|
|
case 0:
|
|
nandOffset = nandSize > 0x200000 ? 0x400000 : 0x200000; //"Legacy" layout
|
|
break;
|
|
}
|
|
|
|
nandOffset *= *emunandIndex; // always 0 for 1st EmuNAND
|
|
|
|
if(fatStart >= nandOffset + roundedMinsizes[ISN3DS ? 1 : 0])
|
|
{
|
|
//Check for RedNAND
|
|
if(!sdmmc_sdcard_readsectors(nandOffset + 1, 1, temp) && memcmp(temp + 0x100, "NCSD", 4) == 0)
|
|
{
|
|
if (configureCtrNandParams)
|
|
{
|
|
emuOffset = nandOffset + 1;
|
|
emuHeader = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
//Check for Gateway EmuNAND
|
|
else if(i != 2 && !sdmmc_sdcard_readsectors(nandOffset + nandSize, 1, temp) && memcmp(temp + 0x100, "NCSD", 4) == 0)
|
|
{
|
|
if (configureCtrNandParams)
|
|
{
|
|
emuOffset = nandOffset;
|
|
emuHeader = nandSize;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(*emunandIndex == 0) break; // See above comments
|
|
}
|
|
|
|
//Fallback to the first EmuNAND if there's no second/third/fourth one, or to SysNAND if there isn't any
|
|
if(*emunandIndex != 0)
|
|
{
|
|
*emunandIndex = 0;
|
|
locateEmuNand(nandType, emunandIndex, configureCtrNandParams);
|
|
}
|
|
else *nandType = FIRMWARE_SYSNAND;
|
|
}
|
|
|
|
static inline u32 getOldSdmmc(u32 *sdmmc, u32 firmVersion)
|
|
{
|
|
switch(firmVersion)
|
|
{
|
|
case 0x18:
|
|
*sdmmc = 0x080D91D8;
|
|
break;
|
|
case 0x1D:
|
|
case 0x1F:
|
|
*sdmmc = 0x080D8CD0;
|
|
break;
|
|
default:
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline u32 getSdmmc(u8 *pos, u32 size, u32 *sdmmc)
|
|
{
|
|
//Look for struct code
|
|
static const u8 pattern[] = {0x21, 0x20, 0x18, 0x20};
|
|
|
|
const u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
|
|
|
if(off == NULL) return 1;
|
|
|
|
*sdmmc = *(u32 *)(off + 9) + *(u32 *)(off + 0xD);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline u32 patchNandRw(u8 *pos, u32 size, u32 hookAddr)
|
|
{
|
|
//Look for read/write code
|
|
static const u8 pattern[] = {0x1E, 0x00, 0xC8, 0x05};
|
|
|
|
u16 *readOffset = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
|
|
|
if(readOffset == NULL) return 1;
|
|
|
|
readOffset -= 3;
|
|
|
|
u16 *writeOffset = (u16 *)memsearch((u8 *)(readOffset + 5), pattern, 0x100, sizeof(pattern));
|
|
|
|
if(writeOffset == NULL) return 1;
|
|
|
|
writeOffset -= 3;
|
|
*readOffset = *writeOffset = 0x4C00;
|
|
readOffset[1] = writeOffset[1] = 0x47A0;
|
|
((u32 *)writeOffset)[1] = ((u32 *)readOffset)[1] = hookAddr;
|
|
|
|
return 0;
|
|
}
|
|
|
|
u32 patchEmuNand(u8 *process9Offset, u32 process9Size, u32 firmVersion)
|
|
{
|
|
u32 ret = 0;
|
|
|
|
//Add the data of the found EmuNAND
|
|
emunandPatchNandOffset = emuOffset;
|
|
emunandPatchNcsdHeaderOffset = emuHeader;
|
|
|
|
//Find and add the SDMMC struct
|
|
u32 sdmmc;
|
|
ret += !ISN3DS && firmVersion < 0x25 ? getOldSdmmc(&sdmmc, firmVersion) : getSdmmc(process9Offset, process9Size, &sdmmc);
|
|
if(!ret) emunandPatchSdmmcStructPtr = sdmmc;
|
|
|
|
//Add EmuNAND hooks
|
|
ret += patchNandRw(process9Offset, process9Size, (u32)emunandPatch);
|
|
|
|
return ret;
|
|
}
|