/* * This file is part of open_agb_firm * Copyright (C) 2021 derrek, profi200 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "drivers/sha.h" #include "mmio.h" void SHA_getState(u32 *const out); ALWAYS_INLINE void waitBusy(const Sha *const sha) { while(sha->cnt & SHA_EN); } void SHA_start(u16 params) { getShaRegs()->cnt = params | SHA_EN; } // TODO: If we call this with less than 64 bytes first and then with // 64 bytes the FIFO triggers a data abort (busy). void SHA_update(const u32 *data, u32 size) { Sha *const sha = getShaRegs(); volatile ShaFifo *const fifo = getShaFifo(sha); while(size & ~(sizeof(ShaFifo) - 1)) // Optimizes better than while(size > (sizeof(ShaFifo) - 1)). { waitBusy(sha); *fifo = *((const ShaFifo*)data); data += sizeof(ShaFifo) / sizeof(*data); size -= sizeof(ShaFifo); } if(size != 0u) { waitBusy(sha); iomemcpy((vu32*)fifo, data, size); } } void SHA_finish(u32 *const hash, u16 endianess) { Sha *const sha = getShaRegs(); sha->cnt = (sha->cnt & SHA_MODE_MASK) | endianess | SHA_FINAL_ROUND; waitBusy(sha); // We don't need to wait on the SHA_FINAL_ROUND bit (tested). SHA_getState(hash); } void SHA_getState(u32 *const out) { Sha *const sha = getShaRegs(); u32 size; switch(sha->cnt & SHA_MODE_MASK) { case SHA_256_MODE: size = 32; break; case SHA_224_MODE: size = 28; break; case SHA_1_MODE: default: // 2 and 3 SHA1. size = 20; } iomemcpy(out, sha->hash, size); } void sha(const u32 *data, u32 size, u32 *const hash, u16 params, u16 hashEndianess) { SHA_start(params); SHA_update(data, size); SHA_finish(hash, hashEndianess); } #ifdef ARM11 // Note: The FIFO is 64 bit capable but 64 bit is slower than 32 bit. /*SHA CDMA test prog: # 4 bytes burst with 16 transfers. Total 64 bytes per burst. # Source incrementing and destination fixed. # Source and destination unprivileged, non-secure data access. MOV CCR, SB16 SS32 SAI SP2 DB16 DS32 DAF DP2 MOV SAR, 0xDEADBEEF MOV DAR, 0x10301000 FLUSHP 11 LP 8 # 128 KiB LP 256 # 16 KiB LD WFP 11, burst STPB 11 LPEND LPEND WMB SEV 1 END void sha_dma(const u32 *data, u32 size, u32 *const hash, u16 params, u16 hashEndianess) { IRQ_registerIsr(IRQ_CDMA_EVENT1, 14, 0, blah); // TODO: Move this elsewhere. // TODO: DMA JIT. SHA_start(params | SHA_I_DMA_EN); do { __wfi(); } while(DMA330_status(1)); // TODO: Use events. while(sha->cnt & SHA_EN); SHA_finish(hash, hashEndianess); }*/ #elif ARM9 #include "arm9/drivers/ndma.h" #include "arm9/drivers/interrupt.h" void sha_dma(const u32 *data, u32 size, u32 *const hash, u16 params, u16 hashEndianess) { // Note: XDMA is quite a bit faster. NdmaCh *const ndmaCh = getNdmaChRegs(2); ndmaCh->sad = (u32)data; ndmaCh->dad = (u32)getShaFifo(getShaRegs()); ndmaCh->tcnt = size / 4; ndmaCh->wcnt = 64 / 4; ndmaCh->bcnt = NDMA_FASTEST; ndmaCh->cnt = NDMA_EN | NDMA_IRQ_EN | NDMA_START_SHA_IN | NDMA_TCNT_MODE | NDMA_BURST(64 / 4) | NDMA_SAD_INC | NDMA_DAD_FIX; SHA_start(params | SHA_I_DMA_EN); do { __wfi(); } while(ndmaCh->cnt & NDMA_EN); // NDMA hardware bug workaround. (void)*((const vu8*)data); SHA_finish(hash, hashEndianess); } #endif // #ifdef ARM11