/** @brief Compare the contents of two buffers. @param pMem1 The first buffer to compare. @param pMem2 The second buffer to compare. @param ulLen The length to compare. @return Zero if the two buffers are the same, otherwise nonzero. */ static int32_t RedMemCmpUnchecked( const void *pMem1, const void *pMem2, uint32_t ulLen) { const uint8_t *pbMem1 = CAST_VOID_PTR_TO_CONST_UINT8_PTR(pMem1); const uint8_t *pbMem2 = CAST_VOID_PTR_TO_CONST_UINT8_PTR(pMem2); uint32_t ulIdx = 0U; int32_t lResult; while((ulIdx < ulLen) && (pbMem1[ulIdx] == pbMem2[ulIdx])) { ulIdx++; } if(ulIdx == ulLen) { lResult = 0; } else if(pbMem1[ulIdx] > pbMem2[ulIdx]) { lResult = 1; } else { lResult = -1; } return lResult; }
/** @brief Write sectors to a disk. @param bVolNum The volume number of the volume whose block device is being written to. @param ullSectorStart The starting sector number. @param ulSectorCount The number of sectors to write. @param pBuffer The buffer from which to write the sector data. @return A negated ::REDSTATUS code indicating the operation result. @retval 0 Operation was successful. */ static REDSTATUS DiskWrite( uint8_t bVolNum, uint64_t ullSectorStart, uint32_t ulSectorCount, const void *pBuffer) { REDSTATUS ret = 0; uint32_t ulSectorIdx = 0U; uint32_t ulSectorSize = gaRedVolConf[bVolNum].ulSectorSize; const uint8_t *pbBuffer = CAST_VOID_PTR_TO_CONST_UINT8_PTR(pBuffer); while(ulSectorIdx < ulSectorCount) { uint32_t ulTransfer = REDMIN(ulSectorCount - ulSectorIdx, MAX_SECTOR_TRANSFER); Ctrl_status cs; cs = sd_mmc_ram_2_mem_multi(bVolNum, (uint32_t)(ullSectorStart + ulSectorIdx), (uint16_t)ulTransfer, &pbBuffer[ulSectorIdx * ulSectorSize]); if(cs != CTRL_GOOD) { ret = -RED_EIO; break; } ulSectorIdx += ulTransfer; } return ret; }
/** @brief Write sectors to a disk. @param bVolNum The volume number of the volume whose block device is being written to. @param ullSectorStart The starting sector number. @param ulSectorCount The number of sectors to write. @param pBuffer The buffer from which to write the sector data. @return A negated ::REDSTATUS code indicating the operation result. @retval 0 Operation was successful. @retval -RED_EIO A disk I/O error occurred. */ static REDSTATUS DiskWrite( uint8_t bVolNum, uint64_t ullSectorStart, uint32_t ulSectorCount, const void *pBuffer) { REDSTATUS ret = 0; uint32_t ulSectorIdx = 0U; uint32_t ulSectorSize = gaRedVolConf[bVolNum].ulSectorSize; const uint8_t *pbBuffer = CAST_VOID_PTR_TO_CONST_UINT8_PTR(pBuffer); while(ulSectorIdx < ulSectorCount) { uint32_t ulTransfer = REDMIN(ulSectorCount - ulSectorIdx, MAX_SECTOR_TRANSFER); DRESULT result; result = disk_write(bVolNum, &pbBuffer[ulSectorIdx * ulSectorSize], (DWORD)(ullSectorStart + ulSectorIdx), (BYTE)ulTransfer); if(result != RES_OK) { ret = -RED_EIO; break; } ulSectorIdx += ulTransfer; } return ret; }
/** @brief Determine whether the metaroot is valid. @param pMR The metaroot buffer. @param pfSectorCRCIsValid Populated with whether the first sector of the metaroot buffer is valid. @return Whether the metaroot is valid. @retval true The metaroot buffer is valid. @retval false The metaroot buffer is invalid. */ static bool MetarootIsValid( METAROOT *pMR, bool *pfSectorCRCIsValid) { bool fRet = false; if(pfSectorCRCIsValid == NULL) { REDERROR(); } else if(pMR == NULL) { REDERROR(); *pfSectorCRCIsValid = false; } #ifdef REDCONF_ENDIAN_SWAP else if(RedRev32(pMR->hdr.ulSignature) != META_SIG_METAROOT) #else else if(pMR->hdr.ulSignature != META_SIG_METAROOT) #endif { *pfSectorCRCIsValid = false; } else { const uint8_t *pbMR = CAST_VOID_PTR_TO_CONST_UINT8_PTR(pMR); uint32_t ulSectorCRC = pMR->ulSectorCRC; uint32_t ulCRC; #ifdef REDCONF_ENDIAN_SWAP ulSectorCRC = RedRev32(ulSectorCRC); #endif /* The sector CRC was zero when the CRC was computed during the transaction, so it must be zero here. */ pMR->ulSectorCRC = 0U; ulCRC = RedCrc32Update(0U, &pbMR[8U], gpRedVolConf->ulSectorSize - 8U); fRet = ulCRC == ulSectorCRC; *pfSectorCRCIsValid = fRet; if(fRet) { if(gpRedVolConf->ulSectorSize < REDCONF_BLOCK_SIZE) { ulCRC = RedCrc32Update(ulCRC, &pbMR[gpRedVolConf->ulSectorSize], REDCONF_BLOCK_SIZE - gpRedVolConf->ulSectorSize); } #ifdef REDCONF_ENDIAN_SWAP ulCRC = RedRev32(ulCRC); #endif fRet = ulCRC == pMR->hdr.ulCRC; } } return fRet; }
/** @brief Write sectors to a disk. @param bVolNum The volume number of the volume whose block device is being written to. @param ullSectorStart The starting sector number. @param ulSectorCount The number of sectors to write. @param pBuffer The buffer from which to write the sector data. @return A negated ::REDSTATUS code indicating the operation result. @retval 0 Operation was successful. @retval -RED_EINVAL The block device is not open. @retval -RED_EIO A disk I/O error occurred. */ static REDSTATUS DiskWrite( uint8_t bVolNum, uint64_t ullSectorStart, uint32_t ulSectorCount, const void *pBuffer) { REDSTATUS ret = 0; F_DRIVER *pDriver = gapFDriver[bVolNum]; if(pDriver == NULL) { ret = -RED_EINVAL; } else { const uint8_t *pbBuffer = CAST_VOID_PTR_TO_CONST_UINT8_PTR(pBuffer); uint32_t ulSectorSize = gaRedVolConf[bVolNum].ulSectorSize; uint32_t ulSectorIdx; int iErr; for(ulSectorIdx = 0U; ulSectorIdx < ulSectorCount; ulSectorIdx++) { /* We have to cast pbBuffer to non-const since the writesector prototype is flawed, using a non-const pointer for the buffer. */ iErr = pDriver->writesector(pDriver, CAST_AWAY_CONST(uint8_t, &pbBuffer[ulSectorIdx * ulSectorSize]), CAST_ULONG(ullSectorStart + ulSectorCount)); if(iErr != 0) { ret = -RED_EIO; break; } } } return ret; }
/** @brief Copy memory from one address to another. This function should only be called from RedMemCpy(). @param pDest The destination buffer. @param pSrc The source buffer. @param ulLen The number of bytes to copy. */ static void RedMemCpyUnchecked( void *pDest, const void *pSrc, uint32_t ulLen) { uint8_t *pbDest = CAST_VOID_PTR_TO_UINT8_PTR(pDest); const uint8_t *pbSrc = CAST_VOID_PTR_TO_CONST_UINT8_PTR(pSrc); uint32_t ulIdx; for(ulIdx = 0U; ulIdx < ulLen; ulIdx++) { pbDest[ulIdx] = pbSrc[ulIdx]; } }
/** @brief Move memory from one address to another. This function should only be called from RedMemMove(). @param pDest The destination buffer. @param pSrc The source buffer. @param ulLen The number of bytes to copy. */ static void RedMemMoveUnchecked( void *pDest, const void *pSrc, uint32_t ulLen) { uint8_t *pbDest = CAST_VOID_PTR_TO_UINT8_PTR(pDest); const uint8_t *pbSrc = CAST_VOID_PTR_TO_CONST_UINT8_PTR(pSrc); uint32_t ulIdx; if(MEMMOVE_MUST_COPY_FORWARD(pbDest, pbSrc)) { /* If the destination is lower than the source with overlapping memory regions, we must copy from start to end in order to copy the memory correctly. Don't use RedMemCpy() to do this. It is possible that RedMemCpy() has been replaced (even though this function has not been replaced) with an implementation that cannot handle any kind of buffer overlap. */ for(ulIdx = 0U; ulIdx < ulLen; ulIdx++) { pbDest[ulIdx] = pbSrc[ulIdx]; } } else { ulIdx = ulLen; while(ulIdx > 0U) { ulIdx--; pbDest[ulIdx] = pbSrc[ulIdx]; } } }
/** @brief Commit a transaction point. @return A negated ::REDSTATUS code indicating the operation result. @retval 0 Operation was successful. @retval -RED_EIO A disk I/O error occurred. */ REDSTATUS RedVolTransact(void) { REDSTATUS ret = 0; REDASSERT(!gpRedVolume->fReadOnly); /* Should be checked by caller. */ if(gpRedCoreVol->fBranched) { gpRedMR->ulFreeBlocks += gpRedCoreVol->ulAlmostFreeBlocks; gpRedCoreVol->ulAlmostFreeBlocks = 0U; ret = RedBufferFlush(0U, gpRedVolume->ulBlockCount); if(ret == 0) { gpRedMR->hdr.ulSignature = META_SIG_METAROOT; gpRedMR->hdr.ullSequence = gpRedVolume->ullSequence; ret = RedVolSeqNumIncrement(); } if(ret == 0) { const uint8_t *pbMR = CAST_VOID_PTR_TO_CONST_UINT8_PTR(gpRedMR); uint32_t ulSectorCRC; #ifdef REDCONF_ENDIAN_SWAP MetaRootEndianSwap(gpRedMR); #endif gpRedMR->ulSectorCRC = 0U; ulSectorCRC = RedCrc32Update(0U, &pbMR[8U], gpRedVolConf->ulSectorSize - 8U); if(gpRedVolConf->ulSectorSize < REDCONF_BLOCK_SIZE) { gpRedMR->hdr.ulCRC = RedCrc32Update(ulSectorCRC, &pbMR[gpRedVolConf->ulSectorSize], REDCONF_BLOCK_SIZE - gpRedVolConf->ulSectorSize); } else { gpRedMR->hdr.ulCRC = ulSectorCRC; } gpRedMR->ulSectorCRC = ulSectorCRC; #ifdef REDCONF_ENDIAN_SWAP gpRedMR->hdr.ulCRC = RedRev32(gpRedMR->hdr.ulCRC); gpRedMR->ulSectorCRC = RedRev32(gpRedMR->ulSectorCRC); #endif /* Flush the block device before writing the metaroot, so that all previously written blocks are guaranteed to be on the media before the metaroot is written. Otherwise, if the block device reorders the writes, the metaroot could reach the media before metadata it points at, creating a window for disk corruption if power is lost. */ ret = RedIoFlush(gbRedVolNum); } if(ret == 0) { ret = RedIoWrite(gbRedVolNum, BLOCK_NUM_FIRST_METAROOT + gpRedCoreVol->bCurMR, 1U, gpRedMR); #ifdef REDCONF_ENDIAN_SWAP MetaRootEndianSwap(gpRedMR); #endif } /* Flush the block device to force the metaroot write to the media. This guarantees the transaction point is really complete before we return. */ if(ret == 0) { ret = RedIoFlush(gbRedVolNum); } /* Toggle to the other metaroot buffer. The working state and committed state metaroot buffers exchange places. */ if(ret == 0) { uint8_t bNextMR = 1U - gpRedCoreVol->bCurMR; gpRedCoreVol->aMR[bNextMR] = *gpRedMR; gpRedCoreVol->bCurMR = bNextMR; gpRedMR = &gpRedCoreVol->aMR[gpRedCoreVol->bCurMR]; gpRedCoreVol->fBranched = false; } CRITICAL_ASSERT(ret == 0); } return ret; }