博客專(zhuān)欄

        EEPW首頁(yè) > 博客 > CRC校驗(yàn) | 程序如何檢查自身完整性?(1)

        CRC校驗(yàn) | 程序如何檢查自身完整性?(1)

        發(fā)布人:魚(yú)鷹談單片機(jī) 時(shí)間:2021-08-12 來(lái)源:工程師 發(fā)布文章

        在一些比較嚴(yán)格的行業(yè)里面,不是說(shuō)你的程序能完成必要功能就可以,還需要添加一些額外的功能,比如最常見(jiàn)的看門(mén)狗功能,它可以在程序死機(jī)時(shí)完成重啟,但也僅僅如此而已。很多異常它是無(wú)法檢查的,比如程序偶然跑飛,ram 異常、flash異常等其他問(wèn)題,只有程序hardfault或者其他嚴(yán)重問(wèn)題導(dǎo)致程無(wú)法喂狗時(shí)才能起作用。

        所以有些產(chǎn)品為了保障安全,會(huì)增加安規(guī)代碼,保證程序能夠正常運(yùn)行(UL/CSA/IEC 60730-1/60335-1 B類(lèi)認(rèn)證)。

        自檢內(nèi)容

        MCU 安全檢查一般包括以下幾個(gè)方面:

        1、CPU 自測(cè)(寄存器測(cè)試)

        2、系統(tǒng)時(shí)鐘頻率測(cè)量(保證時(shí)鐘正常工作,不快也不慢,GD 芯片在短路晶振后,程序暫停運(yùn)行,無(wú)法檢查,但是 ST 芯片會(huì)自動(dòng)切換到內(nèi)部時(shí)鐘,可以由程序檢查這種異常)

        3、RAM 自檢

        4、FLASH 存儲(chǔ)器完整性檢查

        5、獨(dú)立看門(mén)狗、窗口看門(mén)狗檢查

        6、安全相關(guān)變量檢查

        7、中斷檢查

        8、I/O 口檢查

        9、棧檢查

        10、程序流程控制

        11、AD 口檢查

        你會(huì)發(fā)現(xiàn)真要完成這份安規(guī)代碼,難度不是一般的大,不過(guò)一般芯片廠商會(huì)提供相關(guān)參考例程和相關(guān)文檔,但不是說(shuō)有了這些資料就完全沒(méi)有問(wèn)題了。

        比如 ST 提供了一個(gè)參考例子,但是它使用的 HAL 庫(kù)(事實(shí)上它還有標(biāo)準(zhǔn)庫(kù),當(dāng)時(shí)不知道),如果原本程序用的標(biāo)準(zhǔn)庫(kù),那么就需要進(jìn)行移植,這個(gè)工作量也不是一般大(首先要能理解程序,才能進(jìn)行正確移植,而里面的邏輯還是很復(fù)雜的)。如果你不想移植,還有一個(gè)辦法是使用 lib 庫(kù),就是將相關(guān)功能打包成一個(gè)庫(kù),雖然程序會(huì)大一些(畢竟很多底層代碼和原來(lái)的重復(fù)了),但確實(shí)是比較簡(jiǎn)單的方法(前提是 flash 夠大)。

        魚(yú)鷹走的是第一條路,移植,并且將相關(guān)的底層代碼提供了接口,這樣不管是用標(biāo)準(zhǔn)庫(kù)還是 HAL 庫(kù),只要自己實(shí)現(xiàn)這這些特定的接口即可完成。

        另外,參考例子只是實(shí)現(xiàn)了一個(gè)最基本的功能,在真正的產(chǎn)品不一定能適用。比如你的程序負(fù)載大,而里面為了測(cè)量時(shí)鐘頻率,幾百微秒時(shí)間就要進(jìn)入一次中斷(即使是分頻后),如果剛好在中斷產(chǎn)生時(shí),其他程序禁用了中斷,運(yùn)行這些代碼有可能就會(huì)出現(xiàn)問(wèn)題,很容易錯(cuò)過(guò)中斷而導(dǎo)致復(fù)位。

        在我一開(kāi)始移植的時(shí)候就是如此,在一個(gè)簡(jiǎn)單的程序里面可以正常運(yùn)行很長(zhǎng)時(shí)間,但是移植到產(chǎn)品工程里面,時(shí)不時(shí)出現(xiàn)時(shí)鐘檢查不通過(guò)的時(shí)候,導(dǎo)致程序不停重啟,最終魚(yú)鷹通過(guò) DMA 傳輸?shù)姆绞浇鉀Q了這個(gè)問(wèn)題,再也不會(huì)因?yàn)闀r(shí)鐘檢查不通過(guò)導(dǎo)致重啟了。

        另外一個(gè)難點(diǎn)是對(duì) .sct (分散加載)文件的理解,這個(gè)會(huì)在后面介紹。

        安規(guī)相關(guān)的內(nèi)容實(shí)在是太多,要寫(xiě)的話可以寫(xiě)成一個(gè)系列了,如果各位道友感興趣的話,多多轉(zhuǎn)發(fā)支持一下魚(yú)鷹,如果效果不錯(cuò),魚(yú)鷹會(huì)考慮完成后續(xù)的其它部分。(這里有一份比較全面但簡(jiǎn)單一些的參考文章可以看看 http://news.eeworld.com.cn/mp/STM32/a80041.jspx,只介紹如何做,沒(méi)怎么介紹為什么這么做)

        資料

        ST 相關(guān)資料可以查看以下內(nèi)容(www.st.com,下載時(shí)需要注冊(cè)郵箱才行,魚(yú)鷹公眾號(hào)后臺(tái)提供了部分資料,可自行領(lǐng)取)

        《AN4435 應(yīng)用筆記》中文版,《AN277》(ROM Self-Test)

        STM8-SafeCLASSB

        https://www.st.com/en/embedded-software/stm8-safeclassb.html

        STM32-CLASSB-SPL(基于標(biāo)準(zhǔn)外設(shè)庫(kù))

        https://www.st.com/en/embedded-software/stm32-classb-spl.html#tools-software

        X-CUBE-CLASSB(基于HAL庫(kù))

        https://www.st.com/en/embedded-software/x-cube-classb.html(不同版本有不同芯片,比如 2.2.0 版本的是 Fx 相關(guān)的,2.3.0 是H7、G0 相關(guān)的)

        當(dāng)然國(guó)產(chǎn)芯片也一般會(huì)提供例程。

        1.png

        本篇筆記只介紹其中一個(gè)內(nèi)容,即 FLASH 檢查,換句話說(shuō)就是程序完整性檢查。

        FLASH 檢查

        我們以比較復(fù)雜的 boot + app + rtos ,開(kāi)發(fā)環(huán)境 keil 、stm32f103 為例介紹相關(guān)知識(shí)。

        一般 boot 和 app 部分是用不同工程管理的,所以 app 部分代碼只能檢查自身的完整性,而不能檢查 boot 部分。

        并且 app 的 flash 區(qū)也不是完全檢查的,有一小部分是也沒(méi)法檢查的,但這并不影響它的功能(既然已經(jīng)跳轉(zhuǎn)到 app 里面了,那么 boot 部分 flash 即使在運(yùn)行時(shí)有問(wèn)題也不影響功能,而如果變量初始值的flash有問(wèn)題就是關(guān)鍵變量檢查的問(wèn)題了)。

        2.png

        現(xiàn)在就是如何檢查的問(wèn)題了。

        如何檢查 | 基本原理

        校驗(yàn)手段有很多,比如 和校驗(yàn)、MD5 校驗(yàn)、CRC 校驗(yàn),這里我們使用 CRC,因?yàn)橐话阈酒瑑?nèi)部會(huì)內(nèi)置該外設(shè)硬件計(jì)算(如果沒(méi)有,可以純 CPU 計(jì)算)。

        然后我們需要了解完整性檢查的基本原理。

        所謂程序完整性檢查,就是在下載代碼前,先用工具把要校驗(yàn)的部分通過(guò)計(jì)算公式計(jì)算出一個(gè)值,保存在某個(gè)地方(flash),然后程序在運(yùn)行的時(shí)候,自己也去讀取要校驗(yàn)的 flash 部分,通過(guò)同樣的計(jì)算公式計(jì)算出一個(gè)值,然后將這個(gè)值和保存在 flash 里面的值進(jìn)行比較,就可以看出代碼是否存在異常了,有異常及時(shí)處理,沒(méi)有異常就繼續(xù)重新檢查。

        而檢查分成兩個(gè)步驟:

        1、開(kāi)機(jī)時(shí),一次性完成所有計(jì)算,保證運(yùn)行前完整。

        2、正常運(yùn)行時(shí),定時(shí)計(jì)算,每次計(jì)算一個(gè)小塊,當(dāng)計(jì)算完最后一塊時(shí)才比較結(jié)果,成功就重新繼續(xù)計(jì)算,失敗則終止程序運(yùn)行,周而往復(fù)(計(jì)算需要較長(zhǎng)的時(shí)間,分時(shí)計(jì)算可以不影響程序正常功能),這樣可以保證程序在運(yùn)行時(shí)也能檢查 FLASH 的完整性,防止 FLASH 運(yùn)行過(guò)程中破壞掉。

        現(xiàn)在有個(gè)問(wèn)題,CRC 保存在何處才是合適的?

        隨便保存在一個(gè)地方肯定是不行的。假設(shè)這個(gè)位置在要校驗(yàn)代碼部分的里面,那么當(dāng)工具計(jì)算這個(gè)值時(shí),又會(huì)篡改掉校驗(yàn)部分里面的數(shù)據(jù)(因?yàn)槟惆?CRC 值放到里面了),那么你的程序校驗(yàn)時(shí),肯定不通過(guò),因?yàn)槟阕x了一個(gè)被改變的 CRC 值。所以這個(gè)值一定要放在代碼的最后面才行。

        另外前面說(shuō)過(guò),運(yùn)行時(shí)會(huì)一小塊一小塊,所以要保證你的 CRC 值存放位置應(yīng)該在小塊大小的邊界位置上。比如一次計(jì)算 16 字節(jié),那你存放的位置應(yīng)該是 16 的倍數(shù)才是正常的。

        所以,CRC 存放位置存在這兩個(gè)限制。

        另外,如何提前計(jì)算好 CRC 的值呢?IAR 內(nèi)置該功能,而 KEIL 我們可以借助強(qiáng)大的開(kāi)源工具 SRecord《功能強(qiáng)大的 HEX 開(kāi)源轉(zhuǎn)換工具,你值得擁有》(一轉(zhuǎn)眼,這篇文章差不多鴿了四個(gè)多月了)幫助我們計(jì)算。

        基本知識(shí)都了解的差不多了,接下來(lái)就是如何操作的問(wèn)題。

        實(shí)操

        1、固定 CRC 位置。

        我們可以在啟動(dòng)文件的最后加入以下代碼(END 前)

        這里默認(rèn)是 0x3D334398,但會(huì)在后續(xù)修改成正確的 CRC 值

        ;*******************************************************************************
        ; User Checksum - must be placed at the end of memory
        ;*******************************************************************************
                        AREA    CHECKSUM, DATA, READONLY, ALIGN=6
                        EXPORT  __Check_Sum
        ; Alignement here must correspond to the size of tested block at FLASH run time test (16 words ~ 64 bytes)!!!
                        ALIGN
        __Check_Sum     DCD     0x3D334398;                  ; Check sum computed externaly

        這里保證了 __Check_Sum 的地址是 2 ^ 6 大小對(duì)齊,所以你的計(jì)算小塊可以這個(gè)大小,當(dāng)然也可以小一些,比如 2 ^ 5 等。這樣就可以將檢查部分分成固定的小塊,不會(huì)多,也不會(huì)少,剛剛好(必須)。

        那么如何將這個(gè)地址固定在代碼最后呢?這個(gè)時(shí)候就需要我們的 .sct 文件發(fā)揮作用了

        (ClassB_stm32F10x.sct)。
        ER_IROM1 0x08000000 0x10000  {  ; load address = execution address
              *.o (RESET, +First)
              *(InRoot$$Sections)
              .ANY (+RO)
              *.o (CHECKSUM, +Last) ;放置在最后
            }

        我們用了 +Last 將其放置在代碼的最后部分,你想把它放置在 bin 文件最后面?暫時(shí)魚(yú)鷹還沒(méi)想到怎么做,有知道的道友可以告訴魚(yú)鷹(通過(guò) sct 的方式)。

        2、CRC 計(jì)算腳本

        在 windows 叫批處理,.bat ,我們可以在參考例程中找到。crc_gen_keil.bat

        我們需要需改三個(gè)位置

        3.png

        第一個(gè)是你的計(jì)算工具的路徑,里面應(yīng)該要有計(jì)算工具。

        4.png

        第二個(gè)就是你的工程名字,我們通過(guò)下面位置確定(魚(yú)鷹用的 Main):

        5.png

        最后是工程路徑。一般在 Objects 文件夾里面,而 map 文件一般在 Listings 文件夾里面。

        說(shuō)白了,這些變量就是為了讓腳本能夠找到 map、hex 文件和工具。但一般默認(rèn)工程,這兩個(gè)文件可能不在一個(gè)文件夾里面,所以我們可以對(duì)例子中的批處理文件 crc_gen_keil.bat 進(jìn)行適當(dāng)修改。

        6.png

        map 文件的作用是為了讓腳本能夠搜索到 __Check_Sum 的地址,然后就可以計(jì)算 CRC 并修改 HEX 里面這個(gè)值了。

        另外還有新增了一個(gè)變量 HEX_ADRR,當(dāng)我們的計(jì)算位置不是從 0x08000000 開(kāi)始時(shí)(比如 app 起始地址在 0x08009000),我們就可以修改這個(gè)變量值。還有我們希望在計(jì)算完并修改 CRC 后可以自己生成 bin 文件方便我們更新固件,還需要加入轉(zhuǎn)化成 bin 的命令。

        其中為了下載修改(CRC)后的 HEX 文件,我們還需要簡(jiǎn)單修改一下,用于判斷工具是否存在,不存在,直接刪除 hex 和 axf 文件(防止下載未修改的文件)。

        %xxx% 類(lèi)似腳本中的 $xxx

        if not exist %SREC_PATH% (
            echo %SREC_PATH% is not exit, exit
            echo ----------------------------------------del %INPUT_HEX% -- %AXF_FILE% ---------------
            del %INPUT_HEX% %AXF_FILE%
            exit
        )


        *博客內(nèi)容為網(wǎng)友個(gè)人發(fā)布,僅代表博主個(gè)人觀點(diǎn),如有侵權(quán)請(qǐng)聯(lián)系工作人員刪除。

        斷路器相關(guān)文章:斷路器原理


        高壓真空斷路器相關(guān)文章:高壓真空斷路器原理
        漏電斷路器相關(guān)文章:漏電斷路器原理


        關(guān)鍵詞: 單片機(jī)

        相關(guān)推薦

        技術(shù)專(zhuān)區(qū)

        關(guān)閉
        主站蜘蛛池模板: 石屏县| 密云县| 丹江口市| 汉中市| 开封市| 湘乡市| 衡南县| 阿克苏市| 通渭县| 泗阳县| 新竹市| 锡林浩特市| 墨江| 太仓市| 遵义县| 东台市| 中山市| 图木舒克市| 蒲江县| 新竹县| 乌恰县| 延安市| 石河子市| 西和县| 平昌县| 河源市| 浠水县| 蓬溪县| 霍城县| 黄冈市| 怀仁县| 和硕县| 遵义县| 静乐县| 通化县| 民和| 绥芬河市| 肇庆市| 凤庆县| 托克托县| 新巴尔虎右旗|