大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是利用i.MXRT1xxx系列内部DCP引擎计算Hash值时需特别处理L1 D-Cache

  关于i.MXRT1xxx系列内部通用数据协处理器DCP模块,痞子衡之前写过一篇文章 《SNVS Master Key仅在i.MXRT10xx Hab关闭时才能用于DCP加解密》 介绍了DCP基本功能和AES加解密使用注意事项,实际上DCP模块除了对AES加解密算法支持外,还支持经典的Hash算法(SHA-1/SHA-256/CRC32)。

  痞子衡最近支持一个i.MXRT大客户,他们项目里使用了DCP做Hash运算,但会出现概率性Hash校验失败的情况(差不多运行50次,会失败1次),这是什么情况?

一、客户项目基本情况

  先介绍下客户基本情况,他们项目使用的主芯片是i.MXRT1062,并且配置了外部串行Flash存储程序代码(XiP),以及外部SDRAM放置程序数据区(其实主要是做frameBuffer的,但也同时放置了.data段和STACK),项目基于的SDK版本是v2.6.2。

  项目中主要调用了 \SDK_2.6.2_EVK-MIMXRT1060\middleware\mbedtls\library\sha256.c 中的 mbedtls_sha256() 函数,这个函数其实是通过调用 \SDK_2.6.2_EVK-MIMXRT1060\middleware\mbedtls\port\ksdk\ksdk_mbedtls.c 里的一系列底层函数mbedtls_sha256_xx() 来进一步实现的。

  ksdk_mbedtls.c 文件是同时适用Kinetis/LPC/i.MXRT等系列MCU的,不同MCU上硬件引擎不同(比如有LTC/CAAM/CAU3/DCP/HashCrypt)。对于i.MXRT1xxx,硬件引擎就是DCP,这些 mbedtls_sha256_xx() 函数主要调用了 SDK 标准驱动 fsl_dcp.c 里的如下函数:

  1. status_t DCP_HASH_Init(DCP_Type *base, dcp_handle_t *handle, dcp_hash_ctx_t *ctx, dcp_hash_algo_t algo);
  2. status_t DCP_HASH_Update(DCP_Type *base, dcp_hash_ctx_t *ctx, const uint8_t *input, size_t inputSize);
  3. status_t DCP_HASH_Finish(DCP_Type *base, dcp_hash_ctx_t *ctx, uint8_t *output, size_t *outputSize);

二、概率性失败情况分析

  既然是概率性失败的问题,那大概率和Cache处理有关了,我们需要检查下 fsl_dcp.c 驱动是否很好地处理了Cache。让我们打开 \SDK_2.6.2_EVK-MIMXRT1060\boards\evkmimxrt1060\driver_examples\dcp 例程先看一下,在 dcp.c 文件的 main() 函数里可以看到明显的提醒。如果项目里用到了SDRAM,必须将DCache关掉,说明 dcp 驱动并不支持在DCache使能下运行。但显然这个客户项目用到了SDRAM,后来跟客户确认,他们DCache一直是使能的,这显然是有问题的。

  1. int main(void)
  2. {
  3. dcp_config_t dcpConfig;
  4. /* Init hardware*/
  5. BOARD_ConfigMPU();
  6. BOARD_InitPins();
  7. BOARD_BootClockRUN();
  8. BOARD_InitDebugConsole();
  9. /* Data cache must be temporarily disabled to be able to use sdram */
  10. SCB_DisableDCache();
  11. ...

  让我们再次回到SDK版本,在 恩智浦SDK下载主页 可以看到所有i.MXRT1060 SDK历史版本,v2.6.2是2019年7月发布的(这个版本里的dcp驱动版本是v2.1.1),是的,这个客户算是i.MXRT早期客户了。而现在最新的SDK版本已经是v2.9.3(dcp驱动已经升级到v2.1.6),时间快过去两年了,客户并没有实时更新SDK版本。

  早期的 dcp 驱动没有处理DCache,所以其必须在 DCache 关掉的情况下才能正常工作。从v2.1.5开始增加了对 DCache 的处理,这样 dcp 驱动就可以在 DCache 使能的情况下正常工作了。

三、DCP驱动里是如何处理DCache的?

  现在让我们在SDK标准驱动 fsl_dcp.c 中看一下它到底是怎么增加对DCache处理的。

3.1 DCP上下文buffer设置

  使用 dcp 驱动的第一步是DCP模块初始化,即DCP_Init()函数,这个函数会在DCP->CTRL寄存器里将模块全部的四通道都使能以及将上下文(Context)的缓存和通道自切换功能也都开启,其中关于上下文切换有一个重要的私有全局变量 s_dcpContextSwitchingBuffer,这个变量被放置到了NON-CACHE区域(驱动改进处一)。下述DCP->CONTEXT寄存器就是用来存储 s_dcpContextSwitchingBuffer 地址的。

  1. AT_NONCACHEABLE_SECTION_INIT(static dcp_context_t s_dcpContextSwitchingBuffer);
  2. void DCP_Init(DCP_Type *base, const dcp_config_t *config)
  3. {
  4. // 代码省略...
  5. /* use context switching buffer */
  6. base->CONTEXT = (uint32_t)&s_dcpContextSwitchingBuffer;
  7. }

3.2 DCP用户数据in/out buffer设置

  DCP 模块初始化完成后,就是调用 dcp 驱动里的DCP_HASH()函数进行Hash运算,这个函数参数里有两个用户Buffer,一个Input Buffer存放待计算的消息数据,另一个Output Buffer存放计算好的Hash值(SHA256是32bytes),这两个Buffer最好由用户处理放置在NON-CACHE区。

  1. /* Input data for DCP like input and output should be handled properly
  2. * when DCACHE is used (e.g. Clean&Invalidate, use non-cached memory)
  3. */
  4. AT_NONCACHEABLE_SECTION(static uint8_t s_outputSha256[32]);
  5. status_t calc_sha256(const uint8_t *messageBuf, uint32_t messageLen)
  6. {
  7. size_t outLength = sizeof(s_outputSha256);
  8. dcp_handle_t m_handle;
  9. m_handle.channel = kDCP_Channel0;
  10. m_handle.keySlot = kDCP_KeySlot0;
  11. m_handle.swapConfig = kDCP_NoSwap;
  12. memset(&s_outputSha256, 0, outLength);
  13. return DCP_HASH(DCP, &m_handle, kDCP_Sha256, messageBuf, messageLen, s_outputSha256, &outLength);
  14. }

3.3 DCP_HASH()相关代码中DCache处理

  DCP_HASH()函数运行过程中会一直用到一个非常关键的内部结构体 dcp_hash_ctx_internal_t,这个结构体大小为47 Words(包含128byte的待计算消息数据块blk、32bytes实时计算结果runningHash、及其他辅助变量成员)。

  1. /*! internal dcp_hash context structure */
  2. typedef struct _dcp_hash_ctx_internal
  3. {
  4. dcp_hash_block_t blk; /*!< memory buffer. only full blocks are written to DCP during hash updates */
  5. size_t blksz; /*!< number of valid bytes in memory buffer */
  6. dcp_hash_algo_t algo; /*!< selected algorithm from the set of supported algorithms */
  7. dcp_hash_algo_state_t state; /*!< finite machine state of the hash software process */
  8. uint32_t fullMessageSize; /*!< track message size */
  9. uint32_t ctrl0; /*!< HASH_INIT and HASH_TERM flags */
  10. uint32_t runningHash[9]; /*!< running hash. up to SHA-256 plus size, that is 36 bytes. */
  11. dcp_handle_t *handle;
  12. } dcp_hash_ctx_internal_t;

  dcp 驱动直接定义了 dcp_hash_ctx_t 型局部变量hashCtx,hashCtx空间后续会被用作dcp_hash_ctx_internal_t。旧版本里DCP_HASH_CTX_SIZE值为58,新版本增加到64,这是为了后续L1DCACHE的LINE对齐(驱动改进处二)。

  1. /*! @brief DCP HASH Context size. */
  2. #define DCP_HASH_CTX_SIZE 64
  3. /*! @brief Storage type used to save hash context. */
  4. typedef struct _dcp_hash_ctx_t
  5. {
  6. uint32_t x[DCP_HASH_CTX_SIZE];
  7. } dcp_hash_ctx_t;
  8. status_t DCP_HASH(DCP_Type *base, dcp_handle_t *handle, dcp_hash_algo_t algo, const uint8_t *input, size_t inputSize, uint8_t *output, size_t *outputSize)
  9. {
  10. dcp_hash_ctx_t hashCtx = {0};
  11. status_t status;
  12. status = DCP_HASH_Init(base, handle, &hashCtx, algo);
  13. status = DCP_HASH_Update(base, &hashCtx, input, inputSize);
  14. status = DCP_HASH_Finish(base, &hashCtx, output, outputSize);
  15. // ...
  16. }
  17. status_t DCP_HASH_Init/Update/Finish(...,dcp_hash_ctx_t *ctx,...)
  18. {
  19. dcp_hash_ctx_internal_t *ctxInternal;
  20. /* Align structure on DCACHE line*/
  21. #if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) && defined(DCP_USE_DCACHE) && (DCP_USE_DCACHE == 1U)
  22. ctxInternal = (dcp_hash_ctx_internal_t *)(uint32_t)((uint8_t *)ctx + FSL_FEATURE_L1DCACHE_LINESIZE_BYTE);
  23. #else
  24. ctxInternal = (dcp_hash_ctx_internal_t *)(uint32_t)ctx;
  25. #endif
  26. // 代码省略...
  27. }

  DCP_HASH()函数中启动DCP引擎去计算消息块数据前,都会调用 DCACHE_InvalidateByRange() 函数对 ctxInternal 所占空间做清理(驱动改进处三)。启动DCP引擎工作一次的函数是dcp_hash_update(),这个函数会利用 dcp_work_packet_t 型结构体变量,对于这个结构,代码中也同样做了L1DCACHE对齐处理(驱动改进处四):

  1. /*! @brief DCP's work packet. */
  2. typedef struct _dcp_work_packet
  3. {
  4. uint32_t nextCmdAddress;
  5. uint32_t control0;
  6. uint32_t control1;
  7. uint32_t sourceBufferAddress;
  8. uint32_t destinationBufferAddress;
  9. uint32_t bufferSize;
  10. uint32_t payloadPointer;
  11. uint32_t status;
  12. } dcp_work_packet_t;
  13. #if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) && defined(DCP_USE_DCACHE) && (DCP_USE_DCACHE == 1U)
  14. static inline uint32_t *DCP_FindCacheLine(uint8_t *dcpWorkExt)
  15. {
  16. while (0U != ((uint32_t)dcpWorkExt & ((uint32_t)FSL_FEATURE_L1DCACHE_LINESIZE_BYTE - 1U)))
  17. {
  18. dcpWorkExt++;
  19. }
  20. return (uint32_t *)(uint32_t)dcpWorkExt;
  21. }
  22. #endif
  23. static status_t dcp_hash_update(DCP_Type *base, dcp_hash_ctx_internal_t *ctxInternal, const uint8_t *msg, size_t size)
  24. {
  25. status_t completionStatus = kStatus_Fail;
  26. /* Use extended DCACHE line size aligned structure */
  27. #if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) && defined(DCP_USE_DCACHE) && (DCP_USE_DCACHE == 1U)
  28. dcp_work_packet_t *dcpWork;
  29. uint8_t dcpWorkExt[sizeof(dcp_work_packet_t) + FSL_FEATURE_L1DCACHE_LINESIZE_BYTE] = {0U};
  30. dcpWork = (dcp_work_packet_t *)(uint32_t)DCP_FindCacheLine(dcpWorkExt);
  31. #else
  32. dcp_work_packet_t dcpWorkPacket = {0};
  33. dcp_work_packet_t *dcpWork = &dcpWorkPacket;
  34. #endif
  35. do
  36. {
  37. completionStatus = dcp_hash_update_non_blocking(base, ctxInternal, dcpWork, msg, size);
  38. } while (completionStatus == (int32_t)kStatus_DCP_Again);
  39. completionStatus = DCP_WaitForChannelComplete(base, ctxInternal->handle);
  40. ctxInternal->ctrl0 = 0;
  41. return (completionStatus);
  42. }

  至此,利用i.MXRT1xxx系列内部DCP引擎计算Hash值时需特别处理L1 D-Cache痞子衡便介绍完毕了,掌声在哪里~~~

欢迎订阅

文章会同时发布到我的 博客园主页CSDN主页知乎主页微信公众号 平台上。

微信搜索"痞子衡嵌入式"或者扫描下面二维码,就可以在手机上第一时间看了哦。

痞子衡嵌入式:利用i.MXRT1xxx系列内部DCP引擎计算Hash值时需特别处理L1 D-Cache的更多相关文章

  1. 痞子衡嵌入式:SNVS Master Key仅在i.MXRT10xx Hab关闭时才能用于DCP加解密

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT系列中数据协处理器DCP使用SNVS Master Key加解密的注意事项. i.MXRT不仅仅是处理性能超强的MCU,也是 ...

  2. 痞子衡嵌入式:恩智浦LPC系列MCU开发那些事 - 索引

    大家好,我是痞子衡,是正经搞技术的痞子.本系列痞子衡给大家介绍的是恩智浦LPC系列微控制器相关知识. 恩智浦半导体最早于2003年便开始推出LPC系列MCU,但早期的产品LPC2000/3000系列属 ...

  3. 痞子衡嵌入式:利用i.MXRT1xxx系列ROM提供的FlexSPI driver API可轻松IAP

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT系列ROM中的FlexSPI驱动API实现IAP. 痞子衡的技术交流群里经常有群友提问: i.MXRT中的FlexSPI驱动 ...

  4. 痞子衡嵌入式:了解i.MXRTxxx系列ROM API及其与i.MXRT1xxx系列的差异

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRTxxx系列ROM API设计细节. 痞子衡之前写过两篇文章 <利用i.MXRT1xxx系列ROM提供的FlexSPI ...

  5. 痞子衡嵌入式:系统时钟配置不当会导致i.MXRT1xxx系列下OTFAD加密启动失败

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是系统时钟配置不当会导致i.MXRT1xxx系列下OTFAD加密启动失败问题. 我们知道,i.MXRT1xxx家族早期型号(RT1050/ ...

  6. 痞子衡嵌入式:浅谈i.MXRT1xxx系列MCU时钟相关功能引脚的作用

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT1xxx系列MCU时钟相关功能引脚作用. 如果我们从一颗 MCU 芯片的引脚分类来看芯片功能,大概可以分为三大类:电源.时钟 ...

  7. 痞子衡嵌入式:IVT里的不同entry设置可能会造成i.MXRT1xxx系列启动App后发生异常跑飞

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是IVT里的不同entry设置可能会造成i.MXRT1xxx系列启动App后发生异常跑飞问题的分析解决经验. 事情缘起恩智浦官方论坛上的一 ...

  8. 痞子衡嵌入式:原来i.MXRT1xxx系列里也暗藏了Product ID寄存器

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT1xxx系列里暗藏的Product ID寄存器. MCU 厂商在定义一个产品系列时,通常是会预先规划产品发展路线的(即会有一 ...

  9. 痞子衡嵌入式:利用GPIO模块来测量i.MXRT1xxx的系统中断延迟时间

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT1xxx的系统中断延迟时间. 在 <Cortex-M系统中断延迟及其测量方法简介> 一文里,痞子衡介绍了 Cor ...

随机推荐

  1. es6 快速入门 —— 函数

    其他章节请看: es6 快速入门 系列 函数 函数是所有编程语言的重要组成部分,es6之前函数语法一直没什么变化,遗留了许多问题,javaScript开发者多年来不断抱怨,es6终于决定大力度更新函数 ...

  2. Vuex理解与使用

    1.Vuex是什么 Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式,用于管理页面的数据状态.提供统一数据操作的生态系统.在组件中可以任意获取仓库中的数据.和Vuex类似的还有redux ...

  3. MySQL注入点与SQL语句的关系

    目录 注入位置分类 内联式 - UNION query SQL injection 终止式 - End SQL injection 堆叠式 - Stacked queries SQL injectio ...

  4. Win命令行切换Python版本

    目录 安装2.x 和 3.x 的python 设置系统环境变量 pip的使用 参考 安装2.x 和 3.x 的python 我这里使用anaconda来安装两个版本的python包. conda cr ...

  5. PAT-1018(Public Bike Management)最短路+额外条件+所有最短路中找出满足条件的路径+dijkstra算法

    Public Bike Management PAT-1018 使用一个vector来存储所有最短路的前驱结点,再通过使用dfs和一个额外的vector记录每一条路径 #include<iost ...

  6. MVC base64加密的文件,前端下载

    后端代码: public FileResult OutPutFile(string  base64file,string filename) { buffer = Convert.FromBase64 ...

  7. SpringCloud-服务与注册

    SpringCloud- Eureka服务注册与发现 1.概述 springcloud是一个非常优秀的微服务框架,要管理众多的服务,就需要对这些服务进行治理,管理每个服务与每个服务之间的依赖关系,可以 ...

  8. Python开发环境从零搭建-03-安装Python解释器并配置

    想要从零开始搭建一个Python的开发环境说容易也容易 说难也能难倒一片开发人员,在接下来的一系列视频中,会详细的讲解如何一步步搭建python的开发环境 本文章是搭建环境的第3篇 讲解的内容是:安装 ...

  9. Java中的四种权限修饰符及六种非访问修饰符(简识)

    一.是哪四种访问权限修饰符呢? public > protected > [default] > private (公共的 ) (受保护的) (默认的) (私有的) 二.简单认识四种 ...

  10. weex参考文章

    1官网:https://weex.apache.org/zh/guide/introduction.html 2.weexui   https://alibaba.github.io/weex-ui/ ...