1 /*---------------------------------------------------------------------------*
2 Project: TwlSDK - CARD - libraries
3 File: card_hash.c
4
5 Copyright 2008-2009 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-06-04#$
14 $Rev: 10698 $
15 $Author: okubata_ryoma $
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,void * buffer,u32 length)99 static void CARDi_CompareHash(const void *hash, 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 // If the card is removed and shutdown processing is delayed, only output a warning and clear data here without forcibly stopping program execution
136 //
137 if ((OS_GetBootType() == OS_BOOTTYPE_ROM) && CARD_IsPulledOut())
138 {
139 OS_TWarning("ROM-hash comparation error. (CARD removal)\n");
140 MI_CpuFill(buffer, 0, length);
141 }
142 else
143 {
144 OS_TPanic("ROM-hash comparation error!\n");
145 }
146 }
147 }
148
149 /*---------------------------------------------------------------------------*
150 Name: CARDi_GetHashSectorIndex
151
152 Description: Gets sector number where the specified ROM offset belongs.
153
154 Arguments: context: CARDRomHashContext structure
155 offset: ROM offset
156
157 Returns: Sector number where the specified ROM offset belongs.
158 *---------------------------------------------------------------------------*/
CARDi_GetHashSectorIndex(const CARDRomHashContext * context,u32 offset)159 SDK_INLINE u32 CARDi_GetHashSectorIndex(const CARDRomHashContext *context, u32 offset)
160 {
161 offset -= context->area_ntr.offset;
162 if (offset >= context->area_ntr.length)
163 {
164 offset += (context->area_ntr.offset - context->area_ltd.offset);
165 if (offset < context->area_ltd.length)
166 {
167 offset += context->area_ntr.length;
168 }
169 else
170 {
171 OS_TPanic("specified ROM address is outof-range.(unsafe without secure hash)\n");
172 }
173 }
174 return offset / context->bytes_per_sector;
175 }
176
177 /*---------------------------------------------------------------------------*
178 Name: CARDi_StartLoading
179
180 Description: Starts transfers for only one sector waiting for media load.
181
182 Arguments: context: CARDRomHashContext structure
183
184 Returns: None.
185 *---------------------------------------------------------------------------*/
CARDi_StartLoading(CARDRomHashContext * context)186 static void CARDi_StartLoading(CARDRomHashContext *context)
187 {
188 // Start the asynchronous process here if not already running
189 void *buffer = NULL;
190 u32 offset = 0;
191 u32 length = 0;
192 OSIntrMode bak_cpsr = OS_DisableInterrupts();
193 if (context->recent_load == NULL)
194 {
195 if (context->loading_block)
196 {
197 // A larger buffer has been allocated than actually needed so that asynchronous DMA transfer is possible.
198 // Align offset of the reference target to the table position that is actually required.
199 CARDRomHashBlock *block = context->loading_block;
200 u32 pos = block->offset;
201 u32 mod = (pos & (CARD_ROM_PAGE_SIZE - 1UL));
202 block->hash = &block->hash_aligned[mod];
203 context->recent_load = block;
204 buffer = block->hash_aligned;
205 offset = pos - mod;
206 length = MATH_ROUNDUP(mod + CARD_ROM_HASH_SIZE * context->sectors_per_block, CARD_ROM_PAGE_SIZE);
207 }
208 else if (context->loading_sector)
209 {
210 CARDRomHashSector *sector = context->loading_sector;
211 context->recent_load = sector;
212 buffer = sector->image;
213 offset = sector->offset;
214 length = context->bytes_per_sector;
215 }
216 }
217 (void)OS_RestoreInterrupts(bak_cpsr);
218 // Perform synchronous transfer here if the asynchronous transfer is not supported or currently unusable
219 if (buffer != NULL)
220 {
221 if ((*context->ReadAsync)(context->userdata, buffer, offset, length) == 0)
222 {
223 (void)(*context->ReadSync)(context->userdata, buffer, offset, length);
224 CARD_NotifyRomHashReadAsync(context);
225 }
226 }
227 }
228
229 /*---------------------------------------------------------------------------*
230 Name: CARD_NotifyRomHashReadAsync
231
232 Description: Notifies completion of the device asynchronous read.
233
234 Arguments: context: CARDRomHashContext structure
235
236 Returns: None.
237 *---------------------------------------------------------------------------*/
CARD_NotifyRomHashReadAsync(CARDRomHashContext * context)238 void CARD_NotifyRomHashReadAsync(CARDRomHashContext *context)
239 {
240 OSIntrMode bak_cpsr = OS_DisableInterrupts();
241 // Move list from "waiting to load" to "waiting to verify"
242 if (context->recent_load == context->loading_sector)
243 {
244 CARDRomHashSector *sector = context->loading_sector;
245 context->loading_sector = sector->next;
246 sector->next = context->loaded_sector;
247 context->loaded_sector = sector;
248 }
249 else
250 {
251 CARDRomHashBlock *block = context->loading_block;
252 context->loading_block = block->next;
253 block->next = context->loaded_block;
254 context->loaded_block = block;
255 }
256 context->recent_load = NULL;
257 (void)OS_RestoreInterrupts(bak_cpsr);
258 // Notify wait thread that "waiting to verify" list was added
259 OS_WakeupThreadDirect(context->loader);
260 // Start transfer of next "waiting to load" list, if there is one
261 CARDi_StartLoading(context);
262 }
263
264 /*---------------------------------------------------------------------------*
265 Name: CARDi_TouchRomHashBlock
266
267 Description: Loads hash block data that includes the specified offset.
268 Does nothing if already loaded.
269
270 Arguments: context: CARDRomHashContext structure
271 sector: Sector to access
272
273 Returns: Corresponding hash block data.
274 *---------------------------------------------------------------------------*/
CARDi_TouchRomHashBlock(CARDRomHashContext * context,u32 sector)275 static CARDRomHashBlock* CARDi_TouchRomHashBlock(CARDRomHashContext *context, u32 sector)
276 {
277 // Note that the block region is handled by linking the NTR and LTD regions.
278 // (Do not insert any extra padding or alignment.)
279 u32 index = sector / context->sectors_per_block;
280 // Determine whether the corresponding block data is already loaded
281 CARDRomHashBlock **pp = &context->valid_block;
282 CARDRomHashBlock *block = NULL;
283 for (;; pp = &(*pp)->next)
284 {
285 block = *pp;
286 if (block->index == index)
287 {
288 // Move the touched block data to the top of the list
289 *pp = block->next;
290 block->next = context->valid_block;
291 context->valid_block = block;
292 break;
293 }
294 // If not loaded, destroy the oldest block data and reload
295 else if (block->next == NULL)
296 {
297 // Move to end of "waiting to load" list
298 OSIntrMode bak_cpsr = OS_DisableInterrupts();
299 CARDRomHashBlock **tail = pp;
300 CARDRomHashBlock *loading = block;
301 // Do nothing if the same block is already waiting to load or waiting to verify
302 for (pp = &context->loaded_block; *pp; pp = &(*pp)->next)
303 {
304 if ((*pp)->index == index)
305 {
306 block = (*pp);
307 loading = NULL;
308 break;
309 }
310 }
311 if (loading)
312 {
313 for (pp = &context->loading_block; *pp; pp = &(*pp)->next)
314 {
315 if ((*pp)->index == index)
316 {
317 block = (*pp);
318 loading = NULL;
319 break;
320 }
321 }
322 if (loading)
323 {
324 *tail = NULL;
325 *pp = loading;
326 loading->index = index;
327 loading->offset = context->sector_hash.offset + index * (CARD_ROM_HASH_SIZE * context->sectors_per_block);
328 }
329 }
330 (void)OS_RestoreInterrupts(bak_cpsr);
331 if (loading)
332 {
333 // Check the start timing of the asynchronous load
334 CARDi_StartLoading(context);
335 }
336 break;
337 }
338 }
339 return block;
340 }
341
342 /*---------------------------------------------------------------------------*
343 Name: CARDi_TouchRomHashSector
344
345 Description: Loads the specified sector on the block.
346 Does nothing if already loaded.
347
348 Arguments: context: CARDRomHashContext structure
349 block: CARDRomHashBlock structure
350 offset: ROM offset to access
351
352 Returns: Corresponding sector image or NULL.
353 *---------------------------------------------------------------------------*/
CARDi_TouchRomHashSector(CARDRomHashContext * context,u32 offset)354 static void* CARDi_TouchRomHashSector(CARDRomHashContext *context, u32 offset)
355 {
356 void *image = NULL;
357 CARDRomHashSector **pp = &context->valid_sector;
358 CARDRomHashSector *sector = NULL;
359 u32 index = CARDi_GetHashSectorIndex(context, offset);
360 for (pp = &context->valid_sector; ; pp = &(*pp)->next)
361 {
362 // If the corresponding sector data is already loaded, returns that image
363 if ((*pp)->index == index)
364 {
365 // Move the touched sector data to the top of the list
366 sector = *pp;
367 *pp = (*pp)->next;
368 sector->next = context->valid_sector;
369 context->valid_sector = sector;
370 image = sector->image;
371 break;
372 }
373 // If not loaded, destroy the oldest sector data and reload
374 else if ((*pp)->next == NULL)
375 {
376 // Move to end of "waiting to load" list
377 OSIntrMode bak_cpsr = OS_DisableInterrupts();
378 CARDRomHashSector *loading = *pp;
379 *pp = NULL;
380 for (pp = &context->loading_sector; *pp; pp = &(*pp)->next)
381 {
382 }
383 *pp = loading;
384 loading->index = index;
385 loading->offset = MATH_ROUNDDOWN(offset, context->bytes_per_sector);
386 (void)OS_RestoreInterrupts(bak_cpsr);
387 // Search for the block that will be required for validity test
388 (void)CARDi_TouchRomHashBlock(context, index);
389 // Check the start timing of the asynchronous load
390 CARDi_StartLoading(context);
391 break;
392 }
393 }
394 return image;
395 }
396
397 /*---------------------------------------------------------------------------*
398 Name: CARD_InitRomHashContext
399
400 Description: Initializes the ROM hashing context.
401
402 Arguments: context: CARDRomHashContext structure that must be initialized
403 header: ROM header buffer that held the ROM information necessary for management
404 (No need to keep this because this is referenced only within the function)
405 buffer: Buffer for management information used in the context
406 length: Size of management information buffer
407 (Can be calculated using the CARD_CalcRomHashBufferLength function)
408 sync: Device synchronous read callback function
409 async: Device asynchronous read callback function (if not supported, NULL)
410 userdata: Arbitrary user-defined function passed to the device read callback
411
412 Returns: None.
413 *---------------------------------------------------------------------------*/
CARD_InitRomHashContext(CARDRomHashContext * context,const CARDRomHeaderTWL * header,void * buffer,u32 length,MIDeviceReadFunction sync,MIDeviceReadFunction async,void * userdata)414 void CARD_InitRomHashContext(CARDRomHashContext *context, const CARDRomHeaderTWL *header,
415 void *buffer, u32 length,
416 MIDeviceReadFunction sync, MIDeviceReadFunction async,
417 void *userdata)
418 {
419 // Constant differs by the build
420 const u32 bytes_per_sector = header->digest_table1_size;
421 const u32 sectors_per_block = header->digest_table2_sectors;
422 const u32 master_hash_size = header->digest_tabel2.length;
423
424 // Calculate whether the required variable length memory has been allocated
425 u8 *lo = (u8 *)MATH_ROUNDUP((u32)buffer, 32);
426 u8 *hi = (u8 *)MATH_ROUNDDOWN((u32)&lo[length], 32);
427 u8 *cur = lo;
428 context->master_hash = (u8 *)cur;
429 cur += MATH_ROUNDUP(master_hash_size, 32);
430 context->images = (u8 *)cur;
431 cur += MATH_ROUNDUP(bytes_per_sector * CARD_ROM_HASH_SECTOR_MAX, 32);
432 context->hashes = (u8 *)cur;
433 cur += MATH_ROUNDUP(MATH_ROUNDUP(CARD_ROM_HASH_SIZE * sectors_per_block, CARD_ROM_PAGE_SIZE * 3) * CARD_ROM_HASH_BLOCK_MAX, 32);
434 context->sectors = (CARDRomHashSector *)cur;
435 cur += MATH_ROUNDUP(sizeof(*context->sectors) * CARD_ROM_HASH_SECTOR_MAX, 32);
436 context->blocks = (CARDRomHashBlock *)cur;
437 cur += MATH_ROUNDUP(sizeof(*context->blocks) * CARD_ROM_HASH_BLOCK_MAX, 32);
438 if (cur > hi)
439 {
440 OS_TPanic("cannot allocate memory for ROM-hash from ARENA");
441 }
442 else
443 {
444 // Initialize the basic settings if the memory can be allocated
445 MI_CpuClear32(lo, (u32)(cur - lo));
446 context->area_ntr = header->digest_area_ntr;
447 context->area_ltd = header->digest_area_ltd;
448 context->sector_hash = header->digest_tabel1;
449 context->block_hash = header->digest_tabel2;
450 context->bytes_per_sector = bytes_per_sector;
451 context->sectors_per_block = sectors_per_block;
452 context->block_max = CARD_ROM_HASH_BLOCK_MAX;
453 context->sector_max = CARD_ROM_HASH_SECTOR_MAX;
454 // Initialize the device reader
455 context->ReadSync = sync;
456 context->ReadAsync = async;
457 context->userdata = userdata;
458 #if defined(CARD_SUPPORT_SHA1OPTIMIZE)
459 // Calculates the ipad/opad for hash calculation in advance
460 {
461 u8 ipad[MATH_HASH_BLOCK_SIZE];
462 u8 opad[MATH_HASH_BLOCK_SIZE];
463 int i;
464 for (i = 0; i < MATH_HASH_BLOCK_SIZE; ++i)
465 {
466 ipad[i] = (u8)(CARDiHmacKey[i] ^ 0x36);
467 opad[i] = (u8)(CARDiHmacKey[i] ^ 0x5c);
468 }
469 SVC_SHA1Init(&sha1_ipad_def);
470 SVC_SHA1Init(&sha1_opad_def);
471 SVC_SHA1Update(&sha1_ipad_def, ipad, sizeof(ipad));
472 SVC_SHA1Update(&sha1_opad_def, opad, sizeof(opad));
473 }
474 #endif
475 // Determine validity by loading the master hash
476 (void)(*context->ReadSync)(context->userdata, context->master_hash, context->block_hash.offset, context->block_hash.length);
477 CARDi_CompareHash(header->digest_tabel2_digest, context->master_hash, context->block_hash.length);
478 // Initialize the sector data list
479 {
480 CARDRomHashSector *sectors = context->sectors;
481 int sector_index = 0;
482 int i;
483 for (i = 0; i < context->sector_max; ++i)
484 {
485 sectors[i].next = §ors[i - 1];
486 sectors[i].image = &context->images[i * context->bytes_per_sector];
487 sectors[i].index = 0xFFFFFFFF;
488 }
489 context->sectors[0].next = NULL;
490 context->valid_sector = &context->sectors[context->sector_max - 1];
491 context->loading_sector = NULL;
492 context->loaded_sector = NULL;
493 }
494 // Initialize the block data list
495 {
496 CARDRomHashBlock *blocks = context->blocks;
497 const u32 unit = MATH_ROUNDUP(CARD_ROM_HASH_SIZE * sectors_per_block, CARD_ROM_PAGE_SIZE * 3);
498 int i;
499 for (i = 0; i < context->block_max; ++i)
500 {
501 blocks[i].next = &blocks[i + 1];
502 blocks[i].index = 0xFFFFFFFF;
503 blocks[i].hash_aligned = &context->hashes[i * unit];
504 }
505 context->valid_block = &context->blocks[0];
506 context->blocks[context->block_max - 1].next = NULL;
507 context->loading_block = NULL;
508 context->loaded_block = NULL;
509 }
510 }
511 }
512
513 /*---------------------------------------------------------------------------*
514 Name: CARDi_ReadRomHashImageDirect
515
516 Description: Directly copies to the transfer destination without caching to the hash context.
517
518 Arguments: context: CARDRomHashContext structure
519 buffer: Buffer to transfer to (must be 4-byte aligned)
520 offset: ROM offset to access
521 length: Transfer size
522
523 Returns: None.
524 *---------------------------------------------------------------------------*/
CARDi_ReadRomHashImageDirect(CARDRomHashContext * context,void * buffer,u32 offset,u32 length)525 static void CARDi_ReadRomHashImageDirect(CARDRomHashContext *context, void *buffer, u32 offset, u32 length)
526 {
527 const u32 sectunit = context->bytes_per_sector;
528 const u32 blckunit = context->sectors_per_block;
529 u32 position = offset;
530 u32 end = length + offset;
531 u32 sector = CARDi_GetHashSectorIndex(context, position);
532 while (position < end)
533 {
534 // Synchronously load images of minimum units that can be obtained this time
535 u32 available = (u32)(*context->ReadSync)(context->userdata, buffer, position, end - position);
536 // Access blocks in advance that may be needed in verification this time
537 (void)CARDi_TouchRomHashBlock(context, sector);
538 // Request load preparation for the portion that will be required next
539 if (context->ReadAsync && (position + available < end))
540 {
541 (void)(*context->ReadAsync)(context->userdata, NULL, position + available, end - (position + available));
542 }
543 // Test validity of obtained image
544 while (available >= sectunit)
545 {
546 CARDRomHashBlock *block = CARDi_TouchRomHashBlock(context, sector);
547 u32 slot = sector - block->index * blckunit;
548 while ((slot < blckunit) && (available >= sectunit))
549 {
550 // Verify the hash table in block units here if required
551 if (block != context->valid_block)
552 {
553 OSIntrMode bak_cpsr = OS_DisableInterrupts();
554 while (context->loading_block)
555 {
556 OS_SleepThread(NULL);
557 }
558 if (block == context->loaded_block)
559 {
560 context->loaded_block = block->next;
561 }
562 (void)OS_RestoreInterrupts(bak_cpsr);
563 CARDi_CompareHash(&context->master_hash[block->index * CARD_ROM_HASH_SIZE],
564 block->hash, CARD_ROM_HASH_SIZE * blckunit);
565 block->next = context->valid_block;
566 context->valid_block = block;
567 }
568 // Calculate the image hash
569 CARDi_CompareHash(&block->hash[slot * CARD_ROM_HASH_SIZE], buffer, sectunit);
570 position += sectunit;
571 available -= sectunit;
572 buffer = ((u8 *)buffer) + sectunit;
573 slot += 1;
574 sector += 1;
575 }
576 }
577 }
578 }
579
580 /*---------------------------------------------------------------------------*
581 Name: CARDi_ReadRomHashImageCaching
582
583 Description: Directly copies to the transfer destination while caching to the hash context.
584
585 Arguments: context: CARDRomHashContext structure
586 buffer: Buffer to transfer to
587 offset: ROM offset to access
588 length: Transfer size
589
590 Returns: None.
591 *---------------------------------------------------------------------------*/
CARDi_ReadRomHashImageCaching(CARDRomHashContext * context,void * buffer,u32 offset,u32 length)592 static void CARDi_ReadRomHashImageCaching(CARDRomHashContext *context, void *buffer, u32 offset, u32 length)
593 {
594 while (length > 0)
595 {
596 // Determine whether the next sector has undergone validity test
597 void *image = CARDi_TouchRomHashSector(context, offset);
598 if (image)
599 {
600 // If verified, copy memory as is and go to next
601 u32 max = context->bytes_per_sector;
602 u32 mod = offset - MATH_ROUNDDOWN(offset, max);
603 u32 len = (u32)MATH_MIN(length, (max - mod));
604 CARDi_CopyMemory((u8*)image + mod, buffer, len);
605 buffer = (u8 *)buffer + len;
606 length -= len;
607 offset += len;
608 }
609 else
610 {
611 // 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.
612 // Related block hashes are also automatically read in advance, and the asynchronous transfer has already started if this is possible.
613 //
614 //
615 u32 hit = MATH_ROUNDDOWN(offset, context->bytes_per_sector);
616 while (context->valid_sector && context->valid_block)
617 {
618 hit += context->bytes_per_sector;
619 if (hit >= offset + length)
620 {
621 break;
622 }
623 (void)CARDi_TouchRomHashSector(context, hit);
624 }
625 for (;;)
626 {
627 // Idle until a verification wait list of a load-complete state is generated
628 OSIntrMode bak_cpsr = OS_DisableInterrupts();
629 CARDRomHashBlock *block = NULL;
630 CARDRomHashSector *sector = NULL;
631 while ((context->loading_sector && !context->loaded_sector) ||
632 (context->loading_block && !context->loaded_block))
633 {
634 OS_SleepThread(NULL);
635 }
636 // Process the block before the sector considering dependency relationships
637 block = context->loaded_block;
638 if (block)
639 {
640 context->loaded_block = block->next;
641 }
642 else
643 {
644 sector = context->loaded_sector;
645 if (sector)
646 {
647 context->loaded_sector = sector->next;
648 }
649 }
650 (void)OS_RestoreInterrupts(bak_cpsr);
651 // Compare with the higher table and verify by calculating block or sector hash
652 if (block)
653 {
654 u32 len = CARD_ROM_HASH_SIZE * context->sectors_per_block;
655 CARDi_CompareHash(&context->master_hash[block->index * CARD_ROM_HASH_SIZE], block->hash, len);
656 // Move the touched block data to the top of the list
657 block->next = context->valid_block;
658 context->valid_block = block;
659 }
660 else if (sector)
661 {
662 CARDRomHashBlock *block = CARDi_TouchRomHashBlock(context, sector->index);
663 u32 slot = sector->index - block->index * context->sectors_per_block;
664 CARDi_CompareHash(&block->hash[slot * CARD_ROM_HASH_SIZE], sector->image, context->bytes_per_sector);
665 // Move the touched sector data to the top of the list
666 sector->next = context->valid_sector;
667 context->valid_sector = sector;
668 }
669 // If there is nothing that must be asynchronously verified, end this loop
670 else
671 {
672 break;
673 }
674 }
675 }
676 }
677 }
678
679 /*---------------------------------------------------------------------------*
680 Name: CARD_ReadRomHashImage
681
682 Description: Reads the hash-verified ROM image from the specified offset
683
684 Arguments: context: CARDRomHashContext structure
685 offset: ROM offset to access
686
687 Returns: None.
688 *---------------------------------------------------------------------------*/
CARD_ReadRomHashImage(CARDRomHashContext * context,void * buffer,u32 offset,u32 length)689 void CARD_ReadRomHashImage(CARDRomHashContext *context, void *buffer, u32 offset, u32 length)
690 {
691 // Initialize notification destination from the DMA
692 context->loader = OS_GetCurrentThread();
693 context->recent_load = NULL;
694 // When conducting a large-scale read where at least half of the sector cache could be lost at once, employ a direct transfer mode to speed up processing and protect the cache
695 //
696 if ((length >= context->bytes_per_sector * (CARD_ROM_HASH_SECTOR_MAX / 2))
697 // TODO:
698 // For the time being, do not use the direct transfer mode with a card boot.
699 // (Particular attention to the word boundary alignment of the transfer source and destination is not necessary. However, performance may actually be worse for synchronous ROM transfers, so conditions should be added when necessary.)
700 //
701 //
702 && (OS_GetBootType() != OS_BOOTTYPE_ROM))
703 {
704 // Split the leading end and trailing end portions without aligning the sector boundaries
705 const u32 sectmask = (u32)(context->bytes_per_sector - 1UL);
706 const u32 headlen = (u32)((context->bytes_per_sector - offset) & sectmask);
707 const u32 bodylen = (u32)((length - headlen) & ~sectmask);
708 // Leading End Portion
709 if (headlen > 0)
710 {
711 CARDi_ReadRomHashImageCaching(context, buffer, offset, headlen);
712 offset += headlen;
713 length -= headlen;
714 buffer = ((u8 *)buffer) + headlen;
715 }
716 // Middle Portion
717 if (bodylen > 0)
718 {
719 CARDi_ReadRomHashImageDirect(context, buffer, offset, bodylen);
720 offset += bodylen;
721 length -= bodylen;
722 buffer = ((u8 *)buffer) + bodylen;
723 }
724 }
725 // Process the remainder that could not be used with the direct transfer mode
726 CARDi_ReadRomHashImageCaching(context, buffer, offset, length);
727 }
728 #include <twl/ltdmain_end.h>
729