TwlIPL/build/libraries/mi/ARM9/mi_loader.c
yutaka 625aefddc0 TwlSDKの最新system callに対応
nand_formatterをLIMITEDを一時的にやめる

git-svn-id: file:///Users/lillianskinner/Downloads/platinum/twl/TwlIPL/trunk@61 b08762b0-b915-fc4b-9d8c-17b2551a87ff
2007-10-24 02:24:12 +00:00

473 lines
16 KiB
C

/*---------------------------------------------------------------------------*
Project: TwlIPL - libraries - mi
File: mi_loader.c
Copyright 2007 Nintendo. All rights reserved.
These coded instructions, statements, and computer programs contain
proprietary information of Nintendo of America Inc. and/or Nintendo
Company Ltd., and are protected by Federal copyright law. They may
not be disclosed to third parties or copied or duplicated in any form,
in whole or in part, without the prior written consent of Nintendo.
$Date:: $
$Rev$
$Author$
*---------------------------------------------------------------------------*/
#include <twl.h>
#include <firm.h>
#include <firm/format/format_rom.h>
/*
PROFILE_ENABLE を定義するとある程度のパフォーマンスチェックができます。
利用するためには、main.cかどこかに、u32 profile[256]; u32 pf_cnt; を
定義する必要があります。
*/
//#define PROFILE_ENABLE
#ifdef SDK_FINALROM // FINALROMで無効化
#undef PROFILE_ENABLE
#endif
#ifdef PROFILE_ENABLE
#define PROFILE_PXI_SEND 1000000000
#define PROFILE_PXI_RECV 2000000000
extern u32 profile[];
extern u32 pf_cnt;
#endif
#define PXI_FIFO_TAG_DATA PXI_FIFO_TAG_USER_0
static ROM_Header* const rh = (ROM_Header*)HW_TWL_ROM_HEADER_BUF;
#define HEADER_SIZE 0x1000
#define AUTH_SIZE 0xe00
#define RSA_BLOCK_SIZE 128
#define HASH_UNIT 0x800 // TODO: optimizing to maximize cache efficiency
static const u8 s_digestDefaultKey[ SVC_SHA1_BLOCK_SIZE ] = {
0x21, 0x06, 0xc0, 0xde,
0xba, 0x98, 0xce, 0x3f,
0xa6, 0x92, 0xe3, 0x9d,
0x46, 0xf2, 0xed, 0x01,
0x76, 0xe3, 0xcc, 0x08,
0x56, 0x23, 0x63, 0xfa,
0xca, 0xd4, 0xec, 0xdf,
0x9a, 0x62, 0x78, 0x34,
0x8f, 0x6d, 0x63, 0x3c,
0xfe, 0x22, 0xca, 0x92,
0x20, 0x88, 0x97, 0x23,
0xd2, 0xcf, 0xae, 0xc2,
0x32, 0x67, 0x8d, 0xfe,
0xca, 0x83, 0x64, 0x98,
0xac, 0xfd, 0x3e, 0x37,
0x87, 0x46, 0x58, 0x24,
};
/*---------------------------------------------------------------------------*
Name: CheckRomCertificate
Description: check the certification in the ROM
ROMヘッダに付加された証明書のチェックを行います。
makerom.TWL内のコードに依存します。
Arguments: pool pointer to the SVCSignHeapContext
pCert pointer to the certification
pCAPubKey pointer to the public key for the certification
gameCode initial code
Returns: TRUE if success
*---------------------------------------------------------------------------*/
static BOOL CheckRomCertificate( SVCSignHeapContext* pool, const RomCertificate *pCert, const void* pCAPubKey, u32 gameCode )
{
u8 digest[DIGEST_SIZE_SHA1];
u8 md[DIGEST_SIZE_SHA1];
int i;
BOOL result = TRUE;
// 証明書ヘッダのマジックナンバーチェック
if( pCert->header.magicNumber != TWL_ROM_CERT_MAGIC_NUMBER ||
// 証明書ヘッダとROMヘッダのゲームコード一致チェック
pCert->header.gameCode != gameCode )
{
result = FALSE;
}
// 証明書署名チェック
SVC_DecryptSign( pool, &digest, pCert->sign, pCAPubKey );
// ダイジェストの計算
SVC_CalcSHA1( md, pCert, ROM_CERT_SIGN_OFFSET );
// 比較
for (i = 0; i < DIGEST_SIZE_SHA1; i++)
{
if ( md[i] != digest[i] )
{
result = FALSE;
}
}
return result;
}
/*---------------------------------------------------------------------------*
Name: MI_LoadBuffer
Description: receive data from ARM7 and store(move) via WRAM[B]
LoadBufferメカニズムで、ファイルの内容をARM7から受け取ります。
引数でSVCSHA1Contextを指定していた場合、コピーのついでにSHA1の
計算も行います。
[LoadBufferメカニズム]
WRAM[B]を利用して、ARM7,ARM9間のデータ転送を行います。
WRAM[B]の各スロットをバケツリレー方式で渡します。
1スロット分のデータまたは全データが格納されたとき、ARM7から
FIRM_PXI_ID_LOAD_PIRIODを受信します。
ARM9は受信後にそのスロットの使用権をARM9に変更してデータを
取り出し、完了後にメモリをクリアして(セキュリティ)、使用権を
ARM7に戻します。
[使用条件]
WRAM[B]をロックせず、初期状態としてARM7側に倒しておくこと。
[注意点]
offsetとsizeはARM7から通知されません。別の経路で同期を取ってください。
SRLファイルを読み込む場合は、互いにROMヘッダを参照できれば十分です。
(ROMヘッダ部分は元から知っているはず)
Arguments: dest destination address for received data
size size to load
ctx context for SHA1 if execute SVC_SHA1Update
Returns: TRUE if success
*---------------------------------------------------------------------------*/
static BOOL MI_LoadBuffer(u8* dest, u32 size, SVCSHA1Context *ctx)
{
u8* base = (u8*)HW_FIRM_LOAD_BUFFER_BASE;
static int count = 0;
while (size > 0)
{
u8* src = base + count * HW_FIRM_LOAD_BUFFER_UNIT_SIZE;
u32 unit = size < HW_FIRM_LOAD_BUFFER_UNIT_SIZE ? size : HW_FIRM_LOAD_BUFFER_UNIT_SIZE;
//OS_TPrintf("%s: src=%X, unit=%X\n", __func__, src, unit);
if ( PXI_RecvID() != FIRM_PXI_ID_LOAD_PIRIOD )
{
return FALSE;
}
#ifdef PROFILE_ENABLE
// x2...: after PXI
profile[pf_cnt++] = PROFILE_PXI_RECV | FIRM_PXI_ID_LOAD_PIRIOD;
profile[pf_cnt++] = (u32)OS_TicksToMicroSeconds(OS_GetTick());
#endif
MIi_SetWramBankMaster_B(count, MI_WRAM_ARM9);
if (ctx)
{
int done;
for (done = 0; done < unit; done += HASH_UNIT)
{
u8* s = src + done;
u8* d = dest + done;
u32 u = unit < done + HASH_UNIT ? unit - done : HASH_UNIT;
SVC_SHA1Update( ctx, s, u );
MI_CpuCopyFast( s, d, u );
}
}
else
{
MI_CpuCopyFast( src, dest, unit );
}
MI_CpuClearFast( src, unit );
DC_FlushRange( src, unit );
MIi_SetWramBankMaster_B(count, MI_WRAM_ARM7);
count = (count + 1) % HW_FIRM_LOAD_BUFFER_UNIT_NUMS;
size -= unit;
dest += unit;
}
return TRUE;
}
/*---------------------------------------------------------------------------*
Name: MI_LoadModule
Description: receive module from ARM7 and store(move) via WRAM[B]
MI_LoadBufferの上位APIで、引数にSHA1のハッシュ値を渡すことで
SHA1ハッシュチェックを行います。
すでにハッシュ値が分かっていて、ちょうどSHA1の計算範囲全体を
読み込む場合に便利です。
Arguments: dest destination address for received data
size size to load
digest digest to compare
Returns: TRUE if success
*---------------------------------------------------------------------------*/
static /*inline*/ BOOL MI_LoadModule(void* dest, u32 size, const u8 digest[DIGEST_SIZE_SHA1])
{
SVCHMACSHA1Context ctx;
u8 md[DIGEST_SIZE_SHA1];
int i;
BOOL result = TRUE;
SVC_HMACSHA1Init(&ctx, s_digestDefaultKey, SVC_SHA1_BLOCK_SIZE );
if ( !MI_LoadBuffer( dest, size, &ctx.sha1_ctx ) ) // UpdateはSHA1と同じ処理
{
return FALSE;
}
SVC_HMACSHA1GetHash(&ctx, md);
#ifdef PROFILE_ENABLE
// xx: after SHA1
profile[pf_cnt++] = (u32)20202020; // checkpoint
profile[pf_cnt++] = (u32)OS_TicksToMicroSeconds(OS_GetTick());
#endif
for ( i = 0; i < DIGEST_SIZE_SHA1; i++ )
{
if ( md[i] != digest[i] )
{
result = FALSE;
}
}
return result;
}
/*---------------------------------------------------------------------------*
Name: MI_LoadHeader
Description: load header
SRLのROMヘッダ部分をARM7から受け取り、認証します。
受信前に、ARM7から FIRM_PXI_ID_LOAD_HEADER を受信します。
受信後、認証が通ったならARM7へ FIRM_PXI_ID_AUTH_HEADER を送信
します。それ以前に、メインメモリの所定の位置にROMヘッダが格納
されていなければなりません。
続けて、seedデータを16バイト送信します。
makerom.TWLまたはIPLの仕様に依存します。
Arguments: pool pointer to the pool info for SVCSignHeapContext
rsa_key key address
Returns: TRUE if success
*---------------------------------------------------------------------------*/
BOOL MI_LoadHeader( SVCSignHeapContext* pool, const void* rsa_key )
{
SVCSHA1Context ctx;
u8 md[DIGEST_SIZE_SHA1];
SignatureData sd;
int i;
BOOL result = TRUE;
SVC_SHA1Init(&ctx);
#ifdef PROFILE_ENABLE
pf_cnt = 10;
#endif
// load header (hash target)
if ( PXI_RecvID() != FIRM_PXI_ID_LOAD_HEADER ||
#ifdef PROFILE_ENABLE
// 10: after PXI
((profile[pf_cnt++] = PROFILE_PXI_RECV | FIRM_PXI_ID_LOAD_HEADER), FALSE) ||
((profile[pf_cnt++] = (u32)OS_TicksToMicroSeconds(OS_GetTick())), FALSE) ||
#endif
!MI_LoadBuffer( (u8*)rh, AUTH_SIZE, &ctx ) )
{
return FALSE;
}
SVC_SHA1GetHash(&ctx, md);
#ifdef PROFILE_ENABLE
// 1x: after HMAC
profile[pf_cnt++] = (u32)2020202020; // checkpoint
profile[pf_cnt++] = (u32)OS_TicksToMicroSeconds(OS_GetTick());
#endif
// load header (remain)
if ( !MI_LoadBuffer( (u8*)rh + AUTH_SIZE, HEADER_SIZE - AUTH_SIZE, NULL ) )
{
return FALSE;
}
// コンテンツ証明書
if ( CheckRomCertificate( pool, &rh->certificate, rsa_key, *(u32*)rh->s.game_code ) )
{
rsa_key = rh->certificate.pubKeyMod; // ヘッダ用の鍵の取り出し
}
else
{
// とりあえずコンテンツ証明書用の鍵がそのまま使えると仮定
}
// ヘッダ署名チェック
SVC_DecryptSign( pool, &sd, rh->signature, rsa_key );
for (i = 0; i < DIGEST_SIZE_SHA1; i++)
{
if ( md[i] != sd.digest[i] )
{
result = FALSE;
}
}
#ifdef PROFILE_ENABLE
// 1x: after RSA, before PXI
profile[pf_cnt++] = (u32)128128128; // checkpoint
profile[pf_cnt++] = (u32)OS_TicksToMicroSeconds(OS_GetTick());
profile[pf_cnt++] = (u32)PROFILE_PXI_SEND | FIRM_PXI_ID_AUTH_HEADER; // checkpoint
#endif
if ( result )
{
PXI_NotifyID( FIRM_PXI_ID_AUTH_HEADER );
PXI_SendDataByFifo( PXI_FIFO_TAG_DATA, sd.aes_key_seed, AES_BLOCK_SIZE );
// DS互換ヘッダコピー
MI_CpuCopyFast( rh, (void*)HW_ROM_HEADER_BUF, HW_ROM_HEADER_BUF_END-HW_ROM_HEADER_BUF );
}
MI_CpuClear8(&sd, sizeof(sd));
MI_CpuClear8(&md, sizeof(md));
return result;
}
/*---------------------------------------------------------------------------*
Name: MI_LoadStatic
Description: load static binary
ARM9/ARM7のStaticおよびLTD Staticを受信します。
受信前に、ARM7からFIRM_PXI_ID_LOAD_*_STATICを受信します。
受信後、認証が通ったならARM7へFIRM_PXI_ID_AUTH_*_STATICを送信
します。サイズが0の場合は、そのパートのPXI通信すら行いません。
このAPIを呼び出す前に、メインメモリの所定の位置にROMヘッダが
格納されている必要があります。
Arguments: None
Returns: TRUE if success
*---------------------------------------------------------------------------*/
BOOL MI_LoadStatic( void )
{
// load ARM9 static region
if ( rh->s.main_size > 0 )
{
#ifdef PROFILE_ENABLE
// 30: before PXI
pf_cnt = 30;
profile[pf_cnt++] = (u32)OS_TicksToMicroSeconds(OS_GetTick());
#endif
if ( PXI_RecvID() != FIRM_PXI_ID_LOAD_ARM9_STATIC ||
#ifdef PROFILE_ENABLE
// 31: after PXI
((profile[pf_cnt++] = PROFILE_PXI_RECV | FIRM_PXI_ID_LOAD_ARM9_STATIC), FALSE) ||
((profile[pf_cnt++] = (u32)OS_TicksToMicroSeconds(OS_GetTick())), FALSE) ||
#endif
!MI_LoadModule( rh->s.main_ram_address, rh->s.main_size, rh->s.main_static_digest ) )
{
return FALSE;
}
#ifdef PROFILE_ENABLE
// 3x: after PXI
profile[pf_cnt++] = (u32)PROFILE_PXI_SEND | FIRM_PXI_ID_AUTH_ARM9_STATIC; // checkpoint
#endif
PXI_NotifyID( FIRM_PXI_ID_AUTH_ARM9_STATIC );
}
// load ARM7 static region
if ( rh->s.sub_size > 0 )
{
#ifdef PROFILE_ENABLE
// 50: before PXI
pf_cnt = 50;
profile[pf_cnt++] = (u32)OS_TicksToMicroSeconds(OS_GetTick());
#endif
if ( PXI_RecvID() != FIRM_PXI_ID_LOAD_ARM7_STATIC ||
#ifdef PROFILE_ENABLE
// 51: after PXI
((profile[pf_cnt++] = PROFILE_PXI_RECV | FIRM_PXI_ID_LOAD_ARM7_STATIC), FALSE) ||
((profile[pf_cnt++] = (u32)OS_TicksToMicroSeconds(OS_GetTick())), FALSE) ||
#endif
!MI_LoadModule( rh->s.sub_ram_address, rh->s.sub_size, rh->s.sub_static_digest ) )
{
return FALSE;
}
#ifdef PROFILE_ENABLE
// 5x: after PXI
profile[pf_cnt++] = (u32)PROFILE_PXI_SEND | FIRM_PXI_ID_AUTH_ARM7_STATIC; // checkpoint
#endif
PXI_NotifyID( FIRM_PXI_ID_AUTH_ARM7_STATIC );
}
// load ARM9 extended static region
if ( rh->s.main_ltd_size > 0 )
{
#ifdef PROFILE_ENABLE
// 70: before PXI
pf_cnt = 70;
profile[pf_cnt++] = (u32)OS_TicksToMicroSeconds(OS_GetTick());
#endif
if ( PXI_RecvID() != FIRM_PXI_ID_LOAD_ARM9_LTD_STATIC ||
#ifdef PROFILE_ENABLE
// 71: after PXI
((profile[pf_cnt++] = PROFILE_PXI_RECV | FIRM_PXI_ID_LOAD_ARM9_LTD_STATIC), FALSE) ||
((profile[pf_cnt++] = (u32)OS_TicksToMicroSeconds(OS_GetTick())), FALSE) ||
#endif
!MI_LoadModule( rh->s.main_ltd_ram_address, rh->s.main_ltd_size, rh->s.main_ltd_static_digest ) )
{
return FALSE;
}
#ifdef PROFILE_ENABLE
// 7x: after PXI
profile[pf_cnt++] = (u32)PROFILE_PXI_SEND | FIRM_PXI_ID_AUTH_ARM9_LTD_STATIC; // checkpoint
#endif
PXI_NotifyID( FIRM_PXI_ID_AUTH_ARM9_LTD_STATIC );
}
// load ARM7 extended static region
if ( rh->s.sub_ltd_size > 0 )
{
#ifdef PROFILE_ENABLE
// 90: before PXI
pf_cnt = 90;
profile[pf_cnt++] = (u32)OS_TicksToMicroSeconds(OS_GetTick());
#endif
if ( PXI_RecvID() != FIRM_PXI_ID_LOAD_ARM7_LTD_STATIC ||
#ifdef PROFILE_ENABLE
// 91: after PXI
((profile[pf_cnt++] = PROFILE_PXI_RECV | FIRM_PXI_ID_LOAD_ARM7_LTD_STATIC), FALSE) ||
((profile[pf_cnt++] = (u32)OS_TicksToMicroSeconds(OS_GetTick())), FALSE) ||
#endif
!MI_LoadModule( rh->s.sub_ltd_ram_address, rh->s.sub_ltd_size, rh->s.sub_ltd_static_digest ) )
{
return FALSE;
}
#ifdef PROFILE_ENABLE
// 9x: before PXI
profile[pf_cnt++] = (u32)PROFILE_PXI_SEND | FIRM_PXI_ID_AUTH_ARM7_LTD_STATIC; // checkpoint
#endif
PXI_NotifyID( FIRM_PXI_ID_AUTH_ARM7_LTD_STATIC );
}
return TRUE;
}
/*---------------------------------------------------------------------------*
Name: MI_Boot
Description: boot
ROMヘッダの情報を引数に、OSi_Bootを呼び出すだけです。
このAPIを呼び出す前に、メインメモリの所定の位置にROMヘッダが
格納されている必要があります。
Arguments: None
Returns: None
*---------------------------------------------------------------------------*/
void MI_Boot( void )
{
OSi_Boot( rh->s.main_entry_address, (MIHeader_WramRegs*)rh->s.main_wram_config_data );
}