Add mechanism to patch TwlBg on the fly
This commit is contained in:
parent
c055fb6f5e
commit
cf3cf12414
@ -102,6 +102,20 @@ typedef struct
|
|||||||
u8 romFsHash[0x20]; //RomFS superblock SHA-256 hash
|
u8 romFsHash[0x20]; //RomFS superblock SHA-256 hash
|
||||||
} Ncch;
|
} Ncch;
|
||||||
|
|
||||||
|
typedef struct ExeFsFileHeader
|
||||||
|
{
|
||||||
|
char name[8];
|
||||||
|
u32 offset;
|
||||||
|
u32 size;
|
||||||
|
} ExeFsFileHeader;
|
||||||
|
|
||||||
|
typedef struct ExeFsHeader
|
||||||
|
{
|
||||||
|
ExeFsFileHeader fileHeaders[10];
|
||||||
|
u8 _reserved_0xa0[0xC0 - 0xA0];
|
||||||
|
u8 fileHashes[10][32];
|
||||||
|
} ExeFsHeader;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
Ncch ncch;
|
Ncch ncch;
|
||||||
|
@ -264,7 +264,142 @@ void loadHomebrewFirm(u32 pressed)
|
|||||||
launchFirm(wantsScreenInit ? 2 : 1, argv);
|
launchFirm(wantsScreenInit ? 2 : 1, argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void mergeSection0(FirmwareType firmType, u32 firmVersion, bool loadFromStorage)
|
static int lzss_decompress(u8 *end)
|
||||||
|
{
|
||||||
|
unsigned int v1; // r1@2
|
||||||
|
u8 *v2; // r2@2
|
||||||
|
u8 *v3; // r3@2
|
||||||
|
u8 *v4; // r1@2
|
||||||
|
char v5; // r5@4
|
||||||
|
char v6; // t1@4
|
||||||
|
signed int v7; // r6@4
|
||||||
|
int v9; // t1@7
|
||||||
|
u8 *v11; // r3@8
|
||||||
|
int v12; // r12@8
|
||||||
|
int v13; // t1@8
|
||||||
|
int v14; // t1@8
|
||||||
|
unsigned int v15; // r7@8
|
||||||
|
int v16; // r12@8
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
if ( end )
|
||||||
|
{
|
||||||
|
v1 = *((u32 *)end - 2);
|
||||||
|
v2 = &end[*((u32 *)end - 1)];
|
||||||
|
v3 = &end[-(v1 >> 24)];
|
||||||
|
v4 = &end[-(v1 & 0xFFFFFF)];
|
||||||
|
while ( v3 > v4 )
|
||||||
|
{
|
||||||
|
v6 = *(v3-- - 1);
|
||||||
|
v5 = v6;
|
||||||
|
v7 = 8;
|
||||||
|
while ( 1 )
|
||||||
|
{
|
||||||
|
if ( (v7-- < 1) )
|
||||||
|
break;
|
||||||
|
if ( v5 & 0x80 )
|
||||||
|
{
|
||||||
|
v13 = *(v3 - 1);
|
||||||
|
v11 = v3 - 1;
|
||||||
|
v12 = v13;
|
||||||
|
v14 = *(v11 - 1);
|
||||||
|
v3 = v11 - 1;
|
||||||
|
v15 = ((v14 | (v12 << 8)) & 0xFFFF0FFF) + 2;
|
||||||
|
v16 = v12 + 32;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ret = v2[v15];
|
||||||
|
*(v2-- - 1) = ret;
|
||||||
|
v16 -= 16;
|
||||||
|
}
|
||||||
|
while ( !(v16 < 0) );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
v9 = *(v3-- - 1);
|
||||||
|
ret = v9;
|
||||||
|
*(v2-- - 1) = v9;
|
||||||
|
}
|
||||||
|
v5 *= 2;
|
||||||
|
if ( v3 <= v4 )
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct CopyKipResult {
|
||||||
|
u32 cxiSize;
|
||||||
|
u8 *codeDstAddr;
|
||||||
|
u32 codeSize;
|
||||||
|
} CopyKipResult;
|
||||||
|
|
||||||
|
// Copy a KIP, decompressing it in place if necessary (TwlBg)
|
||||||
|
static CopyKipResult copyKip(u8 *dst, const u8 *src, u32 maxSize, bool decompress)
|
||||||
|
{
|
||||||
|
const char *extModuleSizeError = "The external FIRM modules are too large.";
|
||||||
|
CopyKipResult res = { 0 };
|
||||||
|
Cxi *dstCxi = (Cxi *)dst;
|
||||||
|
const Cxi *srcCxi = (const Cxi *)src;
|
||||||
|
|
||||||
|
u32 mediaUnitShift = 9 + srcCxi->ncch.flags[6];
|
||||||
|
u32 totalSizeCompressed = srcCxi->ncch.contentSize << mediaUnitShift;
|
||||||
|
|
||||||
|
if (totalSizeCompressed > maxSize)
|
||||||
|
error(extModuleSizeError);
|
||||||
|
|
||||||
|
// First, copy the compressed KIP to the destination
|
||||||
|
memcpy(dst, src, totalSizeCompressed);
|
||||||
|
|
||||||
|
ExHeader *exh = &dstCxi->exHeader;
|
||||||
|
bool isCompressed = (exh->systemControlInfo.flag & 1) != 0;
|
||||||
|
ExeFsHeader *exefs = (ExeFsHeader *)(dst + (dstCxi->ncch.exeFsOffset << mediaUnitShift));
|
||||||
|
ExeFsFileHeader *fh = &exefs->fileHeaders[0];
|
||||||
|
u8 *codeAddr = (u8 *)exefs + sizeof(ExeFsHeader) + fh->offset;
|
||||||
|
|
||||||
|
if (memcmp(fh->name, ".code\0\0\0", 8) != 0 || fh->offset != 0 || exefs->fileHeaders[1].size != 0)
|
||||||
|
error("One of the external FIRM modules have invalid layout.");
|
||||||
|
|
||||||
|
// If it's already decompressed or we don't need to, there is not much left to do
|
||||||
|
if (!decompress || !isCompressed)
|
||||||
|
{
|
||||||
|
res.cxiSize = totalSizeCompressed;
|
||||||
|
res.codeDstAddr = codeAddr;
|
||||||
|
res.codeSize = fh->size;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
u32 codeSize = exh->systemControlInfo.textCodeSet.size;
|
||||||
|
codeSize += exh->systemControlInfo.roCodeSet.size;
|
||||||
|
codeSize += exh->systemControlInfo.dataCodeSet.size;
|
||||||
|
|
||||||
|
u32 codeSizePadded = ((codeSize + (1 << mediaUnitShift) - 1) >> mediaUnitShift) << mediaUnitShift;
|
||||||
|
u32 newTotalSize = (codeAddr + codeSizePadded) - dst;
|
||||||
|
if (newTotalSize > maxSize)
|
||||||
|
error(extModuleSizeError);
|
||||||
|
|
||||||
|
// Decompress in place
|
||||||
|
lzss_decompress(codeAddr + fh->size);
|
||||||
|
|
||||||
|
// Fill padding just in case
|
||||||
|
memset(codeAddr + codeSize, 0, codeSizePadded - codeSize);
|
||||||
|
|
||||||
|
// Fix fields
|
||||||
|
fh->size = codeSize;
|
||||||
|
dstCxi->ncch.exeFsSize = codeSizePadded >> mediaUnitShift;
|
||||||
|
exh->systemControlInfo.flag &= ~1;
|
||||||
|
dstCxi->ncch.contentSize = newTotalSize >> mediaUnitShift;
|
||||||
|
|
||||||
|
res.cxiSize = newTotalSize;
|
||||||
|
res.codeDstAddr = codeAddr;
|
||||||
|
res.codeSize = codeSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
static void mergeSection0(FirmwareType firmType, u32 firmVersion, bool loadFromStorage)
|
||||||
{
|
{
|
||||||
u32 srcModuleSize,
|
u32 srcModuleSize,
|
||||||
nbModules = 0;
|
nbModules = 0;
|
||||||
@ -314,7 +449,8 @@ static inline void mergeSection0(FirmwareType firmType, u32 firmVersion, bool lo
|
|||||||
const char *extModuleSizeError = "The external FIRM modules are too large.";
|
const char *extModuleSizeError = "The external FIRM modules are too large.";
|
||||||
// SAFE_FIRM only for N3DS and only if ENABLESAFEFIRMROSALINA is on
|
// SAFE_FIRM only for N3DS and only if ENABLESAFEFIRMROSALINA is on
|
||||||
u32 maxModuleSize = !isLgyFirm ? 0x80000 : 0x600000;
|
u32 maxModuleSize = !isLgyFirm ? 0x80000 : 0x600000;
|
||||||
for(u32 i = 0, dstModuleSize; i < nbModules; i++, dst += dstModuleSize, maxModuleSize -= dstModuleSize)
|
u32 dstModuleSize = 0;
|
||||||
|
for(u32 i = 0; i < nbModules; i++)
|
||||||
{
|
{
|
||||||
if(loadFromStorage)
|
if(loadFromStorage)
|
||||||
{
|
{
|
||||||
@ -339,11 +475,22 @@ static inline void mergeSection0(FirmwareType firmType, u32 firmVersion, bool lo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dstModuleSize = moduleList[i].size;
|
// If not successfully loaded from storage, then...
|
||||||
|
|
||||||
if(dstModuleSize > maxModuleSize) error(extModuleSizeError);
|
// Decompress stock TwlBg so that we can patch it
|
||||||
|
bool isStockTwlBg = firmType == TWL_FIRM && strcmp(moduleList[i].name, "TwlBg") == 0;
|
||||||
|
|
||||||
memcpy(dst, moduleList[i].src, dstModuleSize);
|
CopyKipResult copyRes = copyKip(dst, moduleList[i].src, maxModuleSize, isStockTwlBg);
|
||||||
|
|
||||||
|
if (isStockTwlBg)
|
||||||
|
{
|
||||||
|
u32 patchRes = patchTwlBg(copyRes.codeDstAddr, copyRes.codeSize);
|
||||||
|
if (patchRes > 0)
|
||||||
|
error("Failed to apply %d TwlBg patch(es)", patchRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
dst += copyRes.cxiSize;
|
||||||
|
maxModuleSize -= copyRes.cxiSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
//4) Patch kernel to take module size into account
|
//4) Patch kernel to take module size into account
|
||||||
@ -482,11 +629,9 @@ u32 patchTwlFirm(u32 firmVersion, bool loadFromStorage, bool doUnitinfoPatch)
|
|||||||
//Apply UNITINFO patch
|
//Apply UNITINFO patch
|
||||||
if(doUnitinfoPatch) ret += patchUnitInfoValueSet(arm9Section, kernel9Size);
|
if(doUnitinfoPatch) ret += patchUnitInfoValueSet(arm9Section, kernel9Size);
|
||||||
|
|
||||||
if(loadFromStorage)
|
// Also patch TwlBg here
|
||||||
{
|
mergeSection0(TWL_FIRM, 0, loadFromStorage);
|
||||||
mergeSection0(TWL_FIRM, 0, true);
|
|
||||||
firm->section[0].size = 0;
|
firm->section[0].size = 0;
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -766,3 +766,10 @@ u32 patchAgbBootSplash(u8 *pos, u32 size)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 patchTwlBg(u8 *pos, u32 size)
|
||||||
|
{
|
||||||
|
(void)pos;
|
||||||
|
(void)size;
|
||||||
|
return 0;
|
||||||
|
}
|
@ -66,3 +66,4 @@ u32 patchTwlFlashcartChecks(u8 *pos, u32 size, u32 firmVersion);
|
|||||||
u32 patchOldTwlFlashcartChecks(u8 *pos, u32 size);
|
u32 patchOldTwlFlashcartChecks(u8 *pos, u32 size);
|
||||||
u32 patchTwlShaHashChecks(u8 *pos, u32 size);
|
u32 patchTwlShaHashChecks(u8 *pos, u32 size);
|
||||||
u32 patchAgbBootSplash(u8 *pos, u32 size);
|
u32 patchAgbBootSplash(u8 *pos, u32 size);
|
||||||
|
u32 patchTwlBg(u8 *pos, u32 size);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user