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 = §ors[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