CRC校驗 | 程序如何檢查自身完整性?(1)
在一些比較嚴格的行業(yè)里面,不是說(shuō)你的程序能完成必要功能就可以,還需要添加一些額外的功能,比如最常見(jiàn)的看門(mén)狗功能,它可以在程序死機時(shí)完成重啟,但也僅僅如此而已。很多異常它是無(wú)法檢查的,比如程序偶然跑飛,ram 異常、flash異常等其他問(wèn)題,只有程序hardfault或者其他嚴重問(wèn)題導致程無(wú)法喂狗時(shí)才能起作用。
所以有些產(chǎn)品為了保障安全,會(huì )增加安規代碼,保證程序能夠正常運行(UL/CSA/IEC 60730-1/60335-1 B類(lèi)認證)。
自檢內容
MCU 安全檢查一般包括以下幾個(gè)方面:
1、CPU 自測(寄存器測試)
2、系統時(shí)鐘頻率測量(保證時(shí)鐘正常工作,不快也不慢,GD 芯片在短路晶振后,程序暫停運行,無(wú)法檢查,但是 ST 芯片會(huì )自動(dòng)切換到內部時(shí)鐘,可以由程序檢查這種異常)
3、RAM 自檢
4、FLASH 存儲器完整性檢查
5、獨立看門(mén)狗、窗口看門(mén)狗檢查
6、安全相關(guān)變量檢查
7、中斷檢查
8、I/O 口檢查
9、棧檢查
10、程序流程控制
11、AD 口檢查
你會(huì )發(fā)現真要完成這份安規代碼,難度不是一般的大,不過(guò)一般芯片廠(chǎng)商會(huì )提供相關(guān)參考例程和相關(guān)文檔,但不是說(shuō)有了這些資料就完全沒(méi)有問(wèn)題了。
比如 ST 提供了一個(gè)參考例子,但是它使用的 HAL 庫(事實(shí)上它還有標準庫,當時(shí)不知道),如果原本程序用的標準庫,那么就需要進(jìn)行移植,這個(gè)工作量也不是一般大(首先要能理解程序,才能進(jìn)行正確移植,而里面的邏輯還是很復雜的)。如果你不想移植,還有一個(gè)辦法是使用 lib 庫,就是將相關(guān)功能打包成一個(gè)庫,雖然程序會(huì )大一些(畢竟很多底層代碼和原來(lái)的重復了),但確實(shí)是比較簡(jiǎn)單的方法(前提是 flash 夠大)。
魚(yú)鷹走的是第一條路,移植,并且將相關(guān)的底層代碼提供了接口,這樣不管是用標準庫還是 HAL 庫,只要自己實(shí)現這這些特定的接口即可完成。
另外,參考例子只是實(shí)現了一個(gè)最基本的功能,在真正的產(chǎn)品不一定能適用。比如你的程序負載大,而里面為了測量時(shí)鐘頻率,幾百微秒時(shí)間就要進(jìn)入一次中斷(即使是分頻后),如果剛好在中斷產(chǎn)生時(shí),其他程序禁用了中斷,運行這些代碼有可能就會(huì )出現問(wèn)題,很容易錯過(guò)中斷而導致復位。
在我一開(kāi)始移植的時(shí)候就是如此,在一個(gè)簡(jiǎn)單的程序里面可以正常運行很長(cháng)時(shí)間,但是移植到產(chǎn)品工程里面,時(shí)不時(shí)出現時(shí)鐘檢查不通過(guò)的時(shí)候,導致程序不停重啟,最終魚(yú)鷹通過(guò) DMA 傳輸的方式解決了這個(gè)問(wèn)題,再也不會(huì )因為時(shí)鐘檢查不通過(guò)導致重啟了。
另外一個(gè)難點(diǎn)是對 .sct (分散加載)文件的理解,這個(gè)會(huì )在后面介紹。
安規相關(guān)的內容實(shí)在是太多,要寫(xiě)的話(huà)可以寫(xiě)成一個(gè)系列了,如果各位道友感興趣的話(huà),多多轉發(fā)支持一下魚(yú)鷹,如果效果不錯,魚(yú)鷹會(huì )考慮完成后續的其它部分。(這里有一份比較全面但簡(jiǎn)單一些的參考文章可以看看 http://news.eeworld.com.cn/mp/STM32/a80041.jspx,只介紹如何做,沒(méi)怎么介紹為什么這么做)
資料
ST 相關(guān)資料可以查看以下內容(www.st.com,下載時(shí)需要注冊郵箱才行,魚(yú)鷹公眾號后臺提供了部分資料,可自行領(lǐng)?。?/p>
《AN4435 應用筆記》中文版,《AN277》(ROM Self-Test)
STM8-SafeCLASSB
https://www.st.com/en/embedded-software/stm8-safeclassb.html
STM32-CLASSB-SPL(基于標準外設庫)
https://www.st.com/en/embedded-software/stm32-classb-spl.html#tools-software
X-CUBE-CLASSB(基于HAL庫)
https://www.st.com/en/embedded-software/x-cube-classb.html(不同版本有不同芯片,比如 2.2.0 版本的是 Fx 相關(guān)的,2.3.0 是H7、G0 相關(guān)的)
當然國產(chǎn)芯片也一般會(huì )提供例程。
本篇筆記只介紹其中一個(gè)內容,即 FLASH 檢查,換句話(huà)說(shuō)就是程序完整性檢查。
FLASH 檢查
我們以比較復雜的 boot + app + rtos ,開(kāi)發(fā)環(huán)境 keil 、stm32f103 為例介紹相關(guān)知識。
一般 boot 和 app 部分是用不同工程管理的,所以 app 部分代碼只能檢查自身的完整性,而不能檢查 boot 部分。
并且 app 的 flash 區也不是完全檢查的,有一小部分是也沒(méi)法檢查的,但這并不影響它的功能(既然已經(jīng)跳轉到 app 里面了,那么 boot 部分 flash 即使在運行時(shí)有問(wèn)題也不影響功能,而如果變量初始值的flash有問(wèn)題就是關(guān)鍵變量檢查的問(wèn)題了)。
現在就是如何檢查的問(wèn)題了。
如何檢查 | 基本原理
校驗手段有很多,比如 和校驗、MD5 校驗、CRC 校驗,這里我們使用 CRC,因為一般芯片內部會(huì )內置該外設硬件計算(如果沒(méi)有,可以純 CPU 計算)。
然后我們需要了解完整性檢查的基本原理。
所謂程序完整性檢查,就是在下載代碼前,先用工具把要校驗的部分通過(guò)計算公式計算出一個(gè)值,保存在某個(gè)地方(flash),然后程序在運行的時(shí)候,自己也去讀取要校驗的 flash 部分,通過(guò)同樣的計算公式計算出一個(gè)值,然后將這個(gè)值和保存在 flash 里面的值進(jìn)行比較,就可以看出代碼是否存在異常了,有異常及時(shí)處理,沒(méi)有異常就繼續重新檢查。
而檢查分成兩個(gè)步驟:
1、開(kāi)機時(shí),一次性完成所有計算,保證運行前完整。
2、正常運行時(shí),定時(shí)計算,每次計算一個(gè)小塊,當計算完最后一塊時(shí)才比較結果,成功就重新繼續計算,失敗則終止程序運行,周而往復(計算需要較長(cháng)的時(shí)間,分時(shí)計算可以不影響程序正常功能),這樣可以保證程序在運行時(shí)也能檢查 FLASH 的完整性,防止 FLASH 運行過(guò)程中破壞掉。
現在有個(gè)問(wèn)題,CRC 保存在何處才是合適的?
隨便保存在一個(gè)地方肯定是不行的。假設這個(gè)位置在要校驗代碼部分的里面,那么當工具計算這個(gè)值時(shí),又會(huì )篡改掉校驗部分里面的數據(因為你把 CRC 值放到里面了),那么你的程序校驗時(shí),肯定不通過(guò),因為你讀了一個(gè)被改變的 CRC 值。所以這個(gè)值一定要放在代碼的最后面才行。
另外前面說(shuō)過(guò),運行時(shí)會(huì )一小塊一小塊,所以要保證你的 CRC 值存放位置應該在小塊大小的邊界位置上。比如一次計算 16 字節,那你存放的位置應該是 16 的倍數才是正常的。
所以,CRC 存放位置存在這兩個(gè)限制。
另外,如何提前計算好 CRC 的值呢?IAR 內置該功能,而 KEIL 我們可以借助強大的開(kāi)源工具 SRecord《功能強大的 HEX 開(kāi)源轉換工具,你值得擁有》(一轉眼,這篇文章差不多鴿了四個(gè)多月了)幫助我們計算。
基本知識都了解的差不多了,接下來(lái)就是如何操作的問(wèn)題。
實(shí)操
1、固定 CRC 位置。
我們可以在啟動(dòng)文件的最后加入以下代碼(END 前)
這里默認是 0x3D334398,但會(huì )在后續修改成正確的 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 大小對齊,所以你的計算小塊可以這個(gè)大小,當然也可以小一些,比如 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 計算腳本
在 windows 叫批處理,.bat ,我們可以在參考例程中找到。crc_gen_keil.bat
我們需要需改三個(gè)位置
第一個(gè)是你的計算工具的路徑,里面應該要有計算工具。
第二個(gè)就是你的工程名字,我們通過(guò)下面位置確定(魚(yú)鷹用的 Main):
最后是工程路徑。一般在 Objects 文件夾里面,而 map 文件一般在 Listings 文件夾里面。
說(shuō)白了,這些變量就是為了讓腳本能夠找到 map、hex 文件和工具。但一般默認工程,這兩個(gè)文件可能不在一個(gè)文件夾里面,所以我們可以對例子中的批處理文件 crc_gen_keil.bat 進(jìn)行適當修改。
map 文件的作用是為了讓腳本能夠搜索到 __Check_Sum 的地址,然后就可以計算 CRC 并修改 HEX 里面這個(gè)值了。
另外還有新增了一個(gè)變量 HEX_ADRR,當我們的計算位置不是從 0x08000000 開(kāi)始時(shí)(比如 app 起始地址在 0x08009000),我們就可以修改這個(gè)變量值。還有我們希望在計算完并修改 CRC 后可以自己生成 bin 文件方便我們更新固件,還需要加入轉化成 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 )
*博客內容為網(wǎng)友個(gè)人發(fā)布,僅代表博主個(gè)人觀(guān)點(diǎn),如有侵權請聯(lián)系工作人員刪除。
斷路器相關(guān)文章:斷路器原理
高壓真空斷路器相關(guān)文章:高壓真空斷路器原理 漏電斷路器相關(guān)文章:漏電斷路器原理