1 /*---------------------------------------------------------------------------*
2   Project:  TwlSDK - CARD - libraries
3   File:     card_hash.c
4 
5   Copyright 2008 Nintendo. All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law. They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Date:: 2009-01-30#$
14   $Rev: 9944 $
15   $Author: yosizaki $
16 
17  *---------------------------------------------------------------------------*/
18 
19 
20 #include <nitro.h>
21 
22 #include "../include/card_rom.h"
23 
24 
25 // Use MI_CpuCopy, which uses a larger size, only in LIMITED mode to prevent it from being linked in a NITRO environment.
26 #if defined(SDK_TWLLTD)
27 #define CARDi_CopyMemory MI_CpuCopy
28 #else
29 #define CARDi_CopyMemory MI_CpuCopy8
30 #endif
31 
32 
33 // Enable this flag when making HMAC-SHA1 calculation high-speed
34 #if defined(SDK_TWL)
35 #define CARD_SUPPORT_SHA1OPTIMIZE
36 #endif
37 
38 #include <twl/ltdmain_begin.h>
39 
40 /*---------------------------------------------------------------------------*/
41 /* Constants */
42 
43 // Constants determined to be appropriate by the Card library at present.
44 // Proper changes are acceptable depending on the result of the performance calculation
45 static const u32    CARD_ROM_HASH_BLOCK_MAX = 4;
46 static const u32    CARD_ROM_HASH_SECTOR_MAX = 32;
47 
48 #if defined(CARD_SUPPORT_SHA1OPTIMIZE)
49 static SVCSHA1Context   sha1_ipad_def;
50 static SVCSHA1Context   sha1_opad_def;
51 #endif
52 
53 static u8 CARDiHmacKey[] ATTRIBUTE_ALIGN(4) =
54 {
55     0x21, 0x06, 0xc0, 0xde, 0xba, 0x98, 0xce, 0x3f,
56     0xa6, 0x92, 0xe3, 0x9d, 0x46, 0xf2, 0xed, 0x01,
57     0x76, 0xe3, 0xcc, 0x08, 0x56, 0x23, 0x63, 0xfa,
58     0xca, 0xd4, 0xec, 0xdf, 0x9a, 0x62, 0x78, 0x34,
59     0x8f, 0x6d, 0x63, 0x3c, 0xfe, 0x22, 0xca, 0x92,
60     0x20, 0x88, 0x97, 0x23, 0xd2, 0xcf, 0xae, 0xc2,
61     0x32, 0x67, 0x8d, 0xfe, 0xca, 0x83, 0x64, 0x98,
62     0xac, 0xfd, 0x3e, 0x37, 0x87, 0x46, 0x58, 0x24,
63 };
64 
65 /*---------------------------------------------------------------------------*/
66 /* Functions */
67 
68 /*---------------------------------------------------------------------------*
69   Name:         CARD_CalcRomHashBufferLength
70 
71   Description:  Gets the buffer size required in ROM hashing context.
72 
73   Arguments:    header: ROM header buffer that held the ROM information necessary for management
74 
75   Returns:      The buffer size required for the ROM hash context.
76  *---------------------------------------------------------------------------*/
CARD_CalcRomHashBufferLength(const CARDRomHeaderTWL * header)77 u32 CARD_CalcRomHashBufferLength(const CARDRomHeaderTWL *header)
78 {
79     u32     length = 0;
80     length += MATH_ROUNDUP(header->digest_tabel2.length, 32);
81     length += MATH_ROUNDUP(header->digest_table1_size * CARD_ROM_HASH_SECTOR_MAX, 32);
82     length += MATH_ROUNDUP(MATH_ROUNDUP(CARD_ROM_HASH_SIZE * header->digest_table2_sectors, CARD_ROM_PAGE_SIZE * 3) * CARD_ROM_HASH_BLOCK_MAX, 32);
83     length += MATH_ROUNDUP(sizeof(CARDRomHashSector) * CARD_ROM_HASH_SECTOR_MAX, 32);
84     length += MATH_ROUNDUP(sizeof(CARDRomHashBlock) * CARD_ROM_HASH_BLOCK_MAX, 32);
85     return length;
86 }
87 
88 /*---------------------------------------------------------------------------*
89   Name:         CARDi_CompareHash
90 
91   Description:  Validity check using hashing (HMAC-SHA1)
92 
93   Arguments:    hash: Hash value to be used as the standard for comparison
94                 src: Image to be checked
95                 len: Size of the subject
96 
97   Returns:      None.
98  *---------------------------------------------------------------------------*/
CARDi_CompareHash(const void * hash,const void * buffer,u32 length)99 static void CARDi_CompareHash(const void *hash, const void *buffer, u32 length)
100 {
101 #if defined(CARD_SUPPORT_SHA1OPTIMIZE)
102     // Copy the entire structure of the ipad/opad that was calculated in advance .
103     u8      tmphash[CARD_ROM_HASH_SIZE];
104     const u32  *h1 = (const u32 *)hash;
105     const u32  *h2 = (const u32 *)tmphash;
106     u32         dif = 0;
107     SVCSHA1Context  tmpcxt;
108     CARDi_CopyMemory(&sha1_ipad_def, &tmpcxt, sizeof(tmpcxt));
109     SVC_SHA1Update(&tmpcxt, buffer, length);
110     SVC_SHA1GetHash(&tmpcxt, tmphash);
111     CARDi_CopyMemory(&sha1_opad_def, &tmpcxt, sizeof(tmpcxt));
112     SVC_SHA1Update(&tmpcxt, tmphash, sizeof(tmphash));
113     SVC_SHA1GetHash(&tmpcxt, tmphash);
114     dif |= (h1[0] ^ h2[0]);
115     dif |= (h1[1] ^ h2[1]);
116     dif |= (h1[2] ^ h2[2]);
117     dif |= (h1[3] ^ h2[3]);
118     dif |= (h1[4] ^ h2[4]);
119     if (dif != 0)
120 #else
121     u8      tmphash[CARD_ROM_HASH_SIZE];
122 #ifdef SDK_TWL
123     if (OS_IsRunOnTwl())
124     {
125         SVC_CalcHMACSHA1(tmphash, buffer, length, CARDiHmacKey, sizeof(CARDiHmacKey));
126     }
127     else
128 #endif
129     {
130         MATH_CalcHMACSHA1(tmphash, buffer, length, CARDiHmacKey, sizeof(CARDiHmacKey));
131     }
132     if (MI_CpuComp8(hash, tmphash, sizeof(tmphash)) != 0)
133 #endif
134     {
135         OS_TPanic("ROM-hash comparation error!\n");
136     }
137 }
138 
139 /*---------------------------------------------------------------------------*
140   Name:         CARDi_GetHashSectorIndex
141 
142   Description:  Gets sector number where the specified ROM offset belongs.
143 
144   Arguments:    context: CARDRomHashContext structure
145                 offset: ROM offset
146 
147   Returns:      Sector number where the specified ROM offset belongs.
148  *---------------------------------------------------------------------------*/
CARDi_GetHashSectorIndex(const CARDRomHashContext * context,u32 offset)149 SDK_INLINE u32 CARDi_GetHashSectorIndex(const CARDRomHashContext *context, u32 offset)
150 {
151     offset -= context->area_ntr.offset;
152     if (offset >= context->area_ntr.length)
153     {
154         offset += (context->area_ntr.offset - context->area_ltd.offset);
155         if (offset < context->area_ltd.length)
156         {
157             offset += context->area_ntr.length;
158         }
159         else
160         {
161             OS_TPanic("specified ROM address is outof-range.(unsafe without secure hash)\n");
162         }
163     }
164     return offset / context->bytes_per_sector;
165 }
166 
167 /*---------------------------------------------------------------------------*
168   Name:         CARDi_StartLoading
169 
170   Description:  Starts transfers for only one sector waiting for media load.
171 
172   Arguments:    context: CARDRomHashContext structure
173 
174   Returns:      None.
175  *---------------------------------------------------------------------------*/
CARDi_StartLoading(CARDRomHashContext * context)176 static void CARDi_StartLoading(CARDRomHashContext *context)
177 {
178     // Start the asynchronous process here if not already running
179     void   *buffer = NULL;
180     u32     offset = 0;
181     u32     length = 0;
182     OSIntrMode  bak_cpsr = OS_DisableInterrupts();
183     if (context->recent_load == NULL)
184     {
185         if (context->loading_block)
186         {
187             // A larger buffer has been allocated than actually needed so that asynchronous DMA  transfer is possible.
188             // Align offset of the reference target to the table position that is actually required.
189             CARDRomHashBlock   *block = context->loading_block;
190             u32                 pos = block->offset;
191             u32                 mod = (pos & (CARD_ROM_PAGE_SIZE - 1UL));
192             block->hash = &block->hash_aligned[mod];
193             context->recent_load = block;
194             buffer = block->hash_aligned;
195             offset = pos - mod;
196             length = MATH_ROUNDUP(mod + CARD_ROM_HASH_SIZE * context->sectors_per_block, CARD_ROM_PAGE_SIZE);
197         }
198         else if (context->loading_sector)
199         {
200             CARDRomHashSector  *sector = context->loading_sector;
201             context->recent_load = sector;
202             buffer = sector->image;
203             offset = sector->offset;
204             length = context->bytes_per_sector;
205         }
206     }
207     (void)OS_RestoreInterrupts(bak_cpsr);
208     // Perform synchronous transfer here if the asynchronous tranfser is not supported or currently unusable
209     if (buffer != NULL)
210     {
211         if ((*context->ReadAsync)(context->userdata, buffer, offset, length) == 0)
212         {
213             (void)(*context->ReadSync)(context->userdata, buffer, offset, length);
214             CARD_NotifyRomHashReadAsync(context);
215         }
216     }
217 }
218 
219 /*---------------------------------------------------------------------------*
220   Name:         CARD_NotifyRomHashReadAsync
221 
222   Description:  Notifies completion of the device asynchronous read.
223 
224   Arguments:    context: CARDRomHashContext structure
225 
226   Returns:      None.
227  *---------------------------------------------------------------------------*/
CARD_NotifyRomHashReadAsync(CARDRomHashContext * context)228 void CARD_NotifyRomHashReadAsync(CARDRomHashContext *context)
229 {
230     OSIntrMode  bak_cpsr = OS_DisableInterrupts();
231     // Move list from "waiting to load" to "waiting to verify"
232     if (context->recent_load == context->loading_sector)
233     {
234         CARDRomHashSector  *sector = context->loading_sector;
235         context->loading_sector = sector->next;
236         sector->next = context->loaded_sector;
237         context->loaded_sector = sector;
238     }
239     else
240     {
241         CARDRomHashBlock   *block = context->loading_block;
242         context->loading_block = block->next;
243         block->next = context->loaded_block;
244         context->loaded_block = block;
245     }
246     context->recent_load = NULL;
247     (void)OS_RestoreInterrupts(bak_cpsr);
248     // Notify wait thread that "waiting to verify" list was added.
249     OS_WakeupThreadDirect(context->loader);
250     // Start transfer of next "waiting to load" list, if there is one.
251     CARDi_StartLoading(context);
252 }
253 
254 /*---------------------------------------------------------------------------*
255   Name:         CARDi_TouchRomHashBlock
256 
257   Description:  Loads hash block data that includes the specified offset.
258                 Does nothing if already loaded.
259 
260   Arguments:    context: CARDRomHashContext structure
261                 sector: Sector to access
262 
263   Returns:      Corresponding hash block data.
264  *---------------------------------------------------------------------------*/
CARDi_TouchRomHashBlock(CARDRomHashContext * context,u32 sector)265 static CARDRomHashBlock* CARDi_TouchRomHashBlock(CARDRomHashContext *context, u32 sector)
266 {
267     // Note that the block region is handled by linking the NTR and LTD regions
268     // (Do not insert any extra padding or alignment)
269     u32     index = sector / context->sectors_per_block;
270     // Determine whether the corresponding block data is already loaded.
271     CARDRomHashBlock  **pp = &context->valid_block;
272     CARDRomHashBlock   *block = NULL;
273     for (;; pp = &(*pp)->next)
274     {
275         block = *pp;
276         if (block->index == index)
277         {
278             // Move the touched block data to the top of the list.
279             *pp = block->next;
280             block->next = context->valid_block;
281             context->valid_block = block;
282             break;
283         }
284         // If not loaded, destroy the oldest block data and reload.
285         else if (block->next == NULL)
286         {
287             // Move to end of "waiting to load" list
288             OSIntrMode  bak_cpsr = OS_DisableInterrupts();
289             CARDRomHashBlock  **tail = pp;
290             CARDRomHashBlock   *loading = block;
291             // Do nothing if the same block is already waiting to load or waiting to verify.
292             for (pp = &context->loaded_block; *pp; pp = &(*pp)->next)
293             {
294                 if ((*pp)->index == index)
295                 {
296                     block = (*pp);
297                     loading = NULL;
298                     break;
299                 }
300             }
301             if (loading)
302             {
303                 for (pp = &context->loading_block; *pp; pp = &(*pp)->next)
304                 {
305                     if ((*pp)->index == index)
306                     {
307                         block = (*pp);
308                         loading = NULL;
309                         break;
310                     }
311                 }
312                 if (loading)
313                 {
314                     *tail = NULL;
315                     *pp = loading;
316                     loading->index = index;
317                     loading->offset = context->sector_hash.offset + index * (CARD_ROM_HASH_SIZE * context->sectors_per_block);
318                 }
319             }
320             (void)OS_RestoreInterrupts(bak_cpsr);
321             if (loading)
322             {
323                 // Check the start timing of the asynchronous load
324                 CARDi_StartLoading(context);
325             }
326             break;
327         }
328     }
329     return block;
330 }
331 
332 /*---------------------------------------------------------------------------*
333   Name:         CARDi_TouchRomHashSector
334 
335   Description:  Loads the specified sector on the block.
336                 Does nothing if already loaded.
337 
338   Arguments:    context: CARDRomHashContext structure
339                 block: CARDRomHashBlock structure
340                 offset: ROM offset to access
341 
342   Returns:      Corresponding sector image or NULL.
343  *---------------------------------------------------------------------------*/
CARDi_TouchRomHashSector(CARDRomHashContext * context,u32 offset)344 static void* CARDi_TouchRomHashSector(CARDRomHashContext *context, u32 offset)
345 {
346     void               *image = NULL;
347     CARDRomHashSector **pp = &context->valid_sector;
348     CARDRomHashSector  *sector = NULL;
349     u32                 index = CARDi_GetHashSectorIndex(context, offset);
350     for (pp = &context->valid_sector; ; pp = &(*pp)->next)
351     {
352         // If the corresponding sector data is already loaded, returns that image
353         if ((*pp)->index == index)
354         {
355             // Move the touched sector data to the top of the list
356             sector = *pp;
357             *pp = (*pp)->next;
358             sector->next = context->valid_sector;
359             context->valid_sector = sector;
360             image = sector->image;
361             break;
362         }
363         // If not loaded, destroy the oldest sector data and reload
364         else if ((*pp)->next == NULL)
365         {
366             // Move to end of "waiting to load" list
367             OSIntrMode  bak_cpsr = OS_DisableInterrupts();
368             CARDRomHashSector  *loading = *pp;
369             *pp = NULL;
370             for (pp = &context->loading_sector; *pp; pp = &(*pp)->next)
371             {
372             }
373             *pp = loading;
374             loading->index = index;
375             loading->offset = MATH_ROUNDDOWN(offset, context->bytes_per_sector);
376             (void)OS_RestoreInterrupts(bak_cpsr);
377             // Search for the block that will be required for validity test
378             (void)CARDi_TouchRomHashBlock(context, index);
379             // Check the start timing of the asynchronous load
380             CARDi_StartLoading(context);
381             break;
382         }
383     }
384     return image;
385 }
386 
387 /*---------------------------------------------------------------------------*
388   Name:         CARD_InitRomHashContext
389 
390   Description:  Initializes the ROM hashing context.
391 
392   Arguments:    context: CARDRomHashContext structure that must be initialized
393                 header: ROM header buffer that held the ROM information necessary for management
394                            (No need to keep this because this is referenced only within the function)
395                 buffer: Buffer for management information used in the context
396                 length: Size of management information buffer
397                            (Can be calculated using the CARD_CalcRomHashBufferLength function)
398                 sync: Device synchronous read callback function
399                 async: Device asynchronous read callback function (if not supported, NULL)
400                 userdata: Arbitrary user-defined function passed to the device read callback
401 
402   Returns:      None.
403  *---------------------------------------------------------------------------*/
CARD_InitRomHashContext(CARDRomHashContext * context,const CARDRomHeaderTWL * header,void * buffer,u32 length,MIDeviceReadFunction sync,MIDeviceReadFunction async,void * userdata)404 void CARD_InitRomHashContext(CARDRomHashContext *context, const CARDRomHeaderTWL *header,
405                              void *buffer, u32 length,
406                              MIDeviceReadFunction sync, MIDeviceReadFunction async,
407                              void *userdata)
408 {
409     // Constant differs by the build
410     const u32   bytes_per_sector = header->digest_table1_size;
411     const u32   sectors_per_block = header->digest_table2_sectors;
412     const u32   master_hash_size = header->digest_tabel2.length;
413 
414     // Calculate whether the required variable length memory has been allocated
415     u8     *lo = (u8 *)MATH_ROUNDUP((u32)buffer, 32);
416     u8     *hi = (u8 *)MATH_ROUNDDOWN((u32)&lo[length], 32);
417     u8     *cur = lo;
418     context->master_hash = (u8 *)cur;
419     cur += MATH_ROUNDUP(master_hash_size, 32);
420     context->images = (u8 *)cur;
421     cur += MATH_ROUNDUP(bytes_per_sector * CARD_ROM_HASH_SECTOR_MAX, 32);
422     context->hashes = (u8 *)cur;
423     cur += MATH_ROUNDUP(MATH_ROUNDUP(CARD_ROM_HASH_SIZE * sectors_per_block, CARD_ROM_PAGE_SIZE * 3) * CARD_ROM_HASH_BLOCK_MAX, 32);
424     context->sectors = (CARDRomHashSector *)cur;
425     cur += MATH_ROUNDUP(sizeof(*context->sectors) * CARD_ROM_HASH_SECTOR_MAX, 32);
426     context->blocks = (CARDRomHashBlock *)cur;
427     cur += MATH_ROUNDUP(sizeof(*context->blocks) * CARD_ROM_HASH_BLOCK_MAX, 32);
428     if (cur > hi)
429     {
430         OS_TPanic("cannot allocate memory for ROM-hash from ARENA");
431     }
432     else
433     {
434         // Initialize the basic settings if the memory can be allocated
435         MI_CpuClear32(lo, (u32)(cur - lo));
436         context->area_ntr = header->digest_area_ntr;
437         context->area_ltd = header->digest_area_ltd;
438         context->sector_hash = header->digest_tabel1;
439         context->block_hash = header->digest_tabel2;
440         context->bytes_per_sector = bytes_per_sector;
441         context->sectors_per_block = sectors_per_block;
442         context->block_max = CARD_ROM_HASH_BLOCK_MAX;
443         context->sector_max = CARD_ROM_HASH_SECTOR_MAX;
444         // Initialize the device reader
445         context->ReadSync = sync;
446         context->ReadAsync = async;
447         context->userdata = userdata;
448 #if defined(CARD_SUPPORT_SHA1OPTIMIZE)
449         // Calculates the ipad/opad for hash calculation in advance
450         {
451             u8      ipad[MATH_HASH_BLOCK_SIZE];
452             u8      opad[MATH_HASH_BLOCK_SIZE];
453             int     i;
454             for (i = 0; i < MATH_HASH_BLOCK_SIZE; ++i)
455             {
456                 ipad[i] = (u8)(CARDiHmacKey[i] ^ 0x36);
457                 opad[i] = (u8)(CARDiHmacKey[i] ^ 0x5c);
458             }
459             SVC_SHA1Init(&sha1_ipad_def);
460             SVC_SHA1Init(&sha1_opad_def);
461             SVC_SHA1Update(&sha1_ipad_def, ipad, sizeof(ipad));
462             SVC_SHA1Update(&sha1_opad_def, opad, sizeof(opad));
463         }
464 #endif
465         // Determine validity by loading the master hash
466         (void)(*context->ReadSync)(context->userdata, context->master_hash, context->block_hash.offset, context->block_hash.length);
467         CARDi_CompareHash(header->digest_tabel2_digest, context->master_hash, context->block_hash.length);
468         // Initialize the sector data list
469         {
470             CARDRomHashSector  *sectors = context->sectors;
471             int     sector_index = 0;
472             int     i;
473             for (i = 0; i < context->sector_max; ++i)
474             {
475                 sectors[i].next = &sectors[i - 1];
476                 sectors[i].image = &context->images[i * context->bytes_per_sector];
477                 sectors[i].index = 0xFFFFFFFF;
478             }
479             context->sectors[0].next = NULL;
480             context->valid_sector = &context->sectors[context->sector_max - 1];
481             context->loading_sector = NULL;
482             context->loaded_sector = NULL;
483         }
484         // Initialize the block data list
485         {
486             CARDRomHashBlock   *blocks = context->blocks;
487             const u32           unit = MATH_ROUNDUP(CARD_ROM_HASH_SIZE * sectors_per_block, CARD_ROM_PAGE_SIZE * 3);
488             int     i;
489             for (i = 0; i < context->block_max; ++i)
490             {
491                 blocks[i].next = &blocks[i + 1];
492                 blocks[i].index = 0xFFFFFFFF;
493                 blocks[i].hash_aligned = &context->hashes[i * unit];
494             }
495             context->valid_block = &context->blocks[0];
496             context->blocks[context->block_max - 1].next = NULL;
497             context->loading_block = NULL;
498             context->loaded_block = NULL;
499         }
500     }
501 }
502 
503 /*---------------------------------------------------------------------------*
504   Name:         CARDi_ReadRomHashImageDirect
505 
506   Description:  Directly copies to the transfer destination without caching to the hash context.
507 
508   Arguments:    context: CARDRomHashContext structure
509                 buffer: The buffer to transfer to (Must be 4-byte aligned)
510                 offset: ROM offset to access
511                 length: Transfer size
512 
513   Returns:      None.
514  *---------------------------------------------------------------------------*/
CARDi_ReadRomHashImageDirect(CARDRomHashContext * context,void * buffer,u32 offset,u32 length)515 static void CARDi_ReadRomHashImageDirect(CARDRomHashContext *context, void *buffer, u32 offset, u32 length)
516 {
517     const u32   sectunit = context->bytes_per_sector;
518     const u32   blckunit = context->sectors_per_block;
519     u32         position = offset;
520     u32         end = length + offset;
521     u32         sector = CARDi_GetHashSectorIndex(context, position);
522     while (position < end)
523     {
524         // Synchronously load images of minimum units that can be obtained this time
525         u32     available = (u32)(*context->ReadSync)(context->userdata, buffer, position, end - position);
526         // Access blocks in advance that may be needed in verification this time
527         (void)CARDi_TouchRomHashBlock(context, sector);
528         // Request load preparation for the portion that will be required next
529         if (context->ReadAsync && (position + available < end))
530         {
531             (void)(*context->ReadAsync)(context->userdata, NULL, position + available, end - (position + available));
532         }
533         // Test validity of obtained image
534         while (available >= sectunit)
535         {
536             CARDRomHashBlock   *block = CARDi_TouchRomHashBlock(context, sector);
537             u32                 slot = sector - block->index * blckunit;
538             while ((slot < blckunit) && (available >= sectunit))
539             {
540                 // Verify the hash table in block units here if required
541                 if (block != context->valid_block)
542                 {
543                     OSIntrMode  bak_cpsr = OS_DisableInterrupts();
544                     while (context->loading_block)
545                     {
546                         OS_SleepThread(NULL);
547                     }
548                     if (block == context->loaded_block)
549                     {
550                         context->loaded_block = block->next;
551                     }
552                     (void)OS_RestoreInterrupts(bak_cpsr);
553                     CARDi_CompareHash(&context->master_hash[block->index * CARD_ROM_HASH_SIZE],
554                                       block->hash, CARD_ROM_HASH_SIZE * blckunit);
555                     block->next = context->valid_block;
556                     context->valid_block = block;
557                 }
558                 // Calculate the image hash
559                 CARDi_CompareHash(&block->hash[slot * CARD_ROM_HASH_SIZE], buffer, sectunit);
560                 position += sectunit;
561                 available -= sectunit;
562                 buffer = ((u8 *)buffer) + sectunit;
563                 slot += 1;
564                 sector += 1;
565             }
566         }
567     }
568 }
569 
570 /*---------------------------------------------------------------------------*
571   Name:         CARDi_ReadRomHashImageCaching
572 
573   Description:  Directly copies to the transfer destination while caching to the hash context.
574 
575   Arguments:    context: CARDRomHashContext structure
576                 buffer: The buffer to transfer to
577                 offset: ROM offset to access
578                 length: Transfer size
579 
580   Returns:      None.
581  *---------------------------------------------------------------------------*/
CARDi_ReadRomHashImageCaching(CARDRomHashContext * context,void * buffer,u32 offset,u32 length)582 static void CARDi_ReadRomHashImageCaching(CARDRomHashContext *context, void *buffer, u32 offset, u32 length)
583 {
584     while (length > 0)
585     {
586         // Determine whether the next sector has undergone validity test
587         void   *image = CARDi_TouchRomHashSector(context, offset);
588         if (image)
589         {
590             // If verified, copy memory as is and go to next
591             u32     max = context->bytes_per_sector;
592             u32     mod = offset - MATH_ROUNDDOWN(offset, max);
593             u32     len = (u32)MATH_MIN(length, (max - mod));
594             CARDi_CopyMemory((u8*)image + mod, buffer, len);
595             buffer = (u8 *)buffer + len;
596             length -= len;
597             offset += len;
598         }
599         else
600         {
601             // If no hit was found, request to load the sector in advance as much as the capacity allows so that all unverified lists can be processed together.
602             // Related block hash is automatically read first, and if possible the asynchronous transfer has already started.
603             //
604             //
605             u32     hit = MATH_ROUNDDOWN(offset, context->bytes_per_sector);
606             while (context->valid_sector && context->valid_block)
607             {
608                 hit += context->bytes_per_sector;
609                 if (hit >= offset + length)
610                 {
611                     break;
612                 }
613                 (void)CARDi_TouchRomHashSector(context, hit);
614             }
615             for (;;)
616             {
617                 // Idle until a verification wait list of a load-complete state is generated
618                 OSIntrMode          bak_cpsr = OS_DisableInterrupts();
619                 CARDRomHashBlock   *block = NULL;
620                 CARDRomHashSector  *sector = NULL;
621                 while ((context->loading_sector && !context->loaded_sector) ||
622                         (context->loading_block && !context->loaded_block))
623                 {
624                     OS_SleepThread(NULL);
625                 }
626                 // Process the block before the sector considering dependency relationships
627                 block = context->loaded_block;
628                 if (block)
629                 {
630                     context->loaded_block = block->next;
631                 }
632                 else
633                 {
634                     sector = context->loaded_sector;
635                     if (sector)
636                     {
637                         context->loaded_sector = sector->next;
638                     }
639                 }
640                 (void)OS_RestoreInterrupts(bak_cpsr);
641                 // Compare with the higher table and verify by calculating block or sector hash
642                 if (block)
643                 {
644                     u32     len = CARD_ROM_HASH_SIZE * context->sectors_per_block;
645                     CARDi_CompareHash(&context->master_hash[block->index * CARD_ROM_HASH_SIZE], block->hash, len);
646                     // Move the touched block data to the top of the list
647                     block->next = context->valid_block;
648                     context->valid_block = block;
649                 }
650                 else if (sector)
651                 {
652                     CARDRomHashBlock   *block = CARDi_TouchRomHashBlock(context, sector->index);
653                     u32                 slot = sector->index - block->index * context->sectors_per_block;
654                     CARDi_CompareHash(&block->hash[slot * CARD_ROM_HASH_SIZE], sector->image, context->bytes_per_sector);
655                     // Move the touched sector data to the top of the list
656                     sector->next = context->valid_sector;
657                     context->valid_sector = sector;
658                 }
659                 // If there is nothing that must be asynchronously verified, end this loop
660                 else
661                 {
662                     break;
663                 }
664             }
665         }
666     }
667 }
668 
669 /*---------------------------------------------------------------------------*
670   Name:         CARD_ReadRomHashImage
671 
672   Description:  Reads the hash-verified ROM image from the specified offset
673 
674   Arguments:    context: CARDRomHashContext structure
675                 offset: ROM offset to access
676 
677   Returns:      None.
678  *---------------------------------------------------------------------------*/
CARD_ReadRomHashImage(CARDRomHashContext * context,void * buffer,u32 offset,u32 length)679 void CARD_ReadRomHashImage(CARDRomHashContext *context, void *buffer, u32 offset, u32 length)
680 {
681     // Initialize notification destination from the DMA
682     context->loader = OS_GetCurrentThread();
683     context->recent_load = NULL;
684     // If conducting a large-scale read where more than half of the sector cache could be lost all at once, employ a direct transfer mode that is high-speed and protects the cache
685     //
686     if ((length >= context->bytes_per_sector * (CARD_ROM_HASH_SECTOR_MAX / 2))
687         // TODO:
688         //   For the time being, do not use the direct transfer mode with a card boot.
689         //   (No particular attention needs to be paid to the word boundary alignment of the transfer source and transfer destination, but when transferring ROM, when there are synchronized versions, performance actually drops, so conditions should be added when necessary.)
690         //
691         //
692         && (OS_GetBootType() != OS_BOOTTYPE_ROM))
693     {
694         // Split the leading end and trailing end portions without aligning the sector boundaries
695         const u32   sectmask = (u32)(context->bytes_per_sector - 1UL);
696         const u32   headlen = (u32)((context->bytes_per_sector - offset) & sectmask);
697         const u32   bodylen = (u32)((length - headlen) & ~sectmask);
698         // Leading End Portion
699         if (headlen > 0)
700         {
701             CARDi_ReadRomHashImageCaching(context, buffer, offset, headlen);
702             offset += headlen;
703             length -= headlen;
704             buffer = ((u8 *)buffer) + headlen;
705         }
706         // Middle Portion
707         if (bodylen > 0)
708         {
709             CARDi_ReadRomHashImageDirect(context, buffer, offset, bodylen);
710             offset += bodylen;
711             length -= bodylen;
712             buffer = ((u8 *)buffer) + bodylen;
713         }
714     }
715     // Process the remainder that could not be used with the direct transfer mode
716     CARDi_ReadRomHashImageCaching(context, buffer, offset, length);
717 }
718 #include <twl/ltdmain_end.h>
719