Bootloader
“Bootloader”俗稱“引導加載程序”。在我們狹義上說的“Bootloader”是特指嵌入式設備中的引導程序,但是從廣義的角度上來說,PC機中也存在“Bootloader”,比如Windows中的引導工具為“NTLOADER”,“Bootmgr”。
“NTLOADER”是Windows 98和Windows XP時代的引導程序,而Windows Vista,Windows 7和Windows 10操作系統(tǒng)的引導程序則為“Bootmgr”。Linux操作系統(tǒng)也有自己特定使用的引導程序,這就是開源組織GNU推出的Grub(GRand Unified Bootloader簡稱“GRUB”),它可以引導非常多的操作系統(tǒng),功能非常強大。
然而對于普通程序猿來說,PC端的引導程序根本不需要我們涉及,它們往往會隨著操作系統(tǒng)被一并自動安裝進我們的PC,只有當安裝了雙系統(tǒng)的時候,開機會出現啟動管理器讓你選擇進入哪個系統(tǒng)的時候,引導程序才會顯出它的“真身”。
圖1 雙系統(tǒng)的開機引導
在嵌入式設備中,Bootloader程序的裁剪是一門必修課。嵌入式設備中,只要使用了Linux這種操作系統(tǒng)必須要使用引導程序,這些嵌入式Linux操作系統(tǒng)的引導程序用的是一個名叫“U-Boot”的開源軟件。而早在嵌入式Linux剛剛興起的時候,各家的硬件平臺標準化遠遠沒有現在這么高,因此對于不同的硬件平臺,需要修改U-Boot的代碼來適配。
圖2 U-Boot的官方網站
接下來問題來了,為什么這種操作系統(tǒng)一定要用引導程序來引導呢?首先來看下U-Boot這種Bootloader的主要功能,這里主要是介紹早期的U-Boot功能,這些功能是整個嵌入式Linux操作系統(tǒng)成功運行的基礎。U-Boot的運行分為兩個階段:
第一階段是Low level init,即低級別的初始化,這一階段主要完成以下一些工作:
設置異常向量;
初始化內存控制器;
拷貝U-Boot第二階段的功能代碼到RAM空間;
設置堆棧,初始化數據段并跳轉至第二階段引導。
第二階段主要完成的功能有:
初始化Flash設備;
初始化系統(tǒng)內存;
初始化NAND,顯示,網絡等其他設備;
進入Bootloader功能區(qū)(可選,功能可以為硬件測試,下載操作系統(tǒng)內核,下載文件系統(tǒng)等);
將Kernel和根文件系統(tǒng)從Flash映射到RAM中;
設定內核啟動參數和啟動內核。
u-boot.lds
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
.text :
{
*(.__image_copy_start)
CPUDIR/start.o (.text*)
*(.text*)
}
. = ALIGN(4);
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
. = ALIGN(4);
.data : {
*(.data*)
arch/arm/cpu/armv7/start.s中節(jié)選代碼
.globl _start
_start: b reset
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
#ifdef CONFIG_SPL_BUILD
_undefined_instruction: .word _undefined_instruction
_software_interrupt: .word _software_interrupt
_prefetch_abort: .word _prefetch_abort
_data_abort: .word _data_abort
_not_used: .word _not_used
_irq: .word _irq
_fiq: .word _fiq
_pad: .word 0x12345678 /* now 16*4=64 */
#else
.globl _undefined_instruction
_undefined_instruction: .word undefined_instruction
.globl _software_interrupt
_software_interrupt: .word software_interrupt
.globl _prefetch_abort
_prefetch_abort: .word prefetch_abort
.globl _data_abort
_data_abort: .word data_abort
.globl _not_used
_not_used: .word not_used
.globl _irq
_irq: .word irq
.globl _fiq
_fiq: .word fiq
_pad: .word 0x12345678 /* now 16*4=64 */
#endif /* CONFIG_SPL_BUILD */
.global _end_vect
_end_vect:
.balignl 16,0xdeadbeef
在GCC的Make系統(tǒng)中,一個程序的入口是由鏈接文件決定的,也就是我們這里的start.s文件。在ARM系統(tǒng)上電的時候,會根據硬件啟動方式的選擇,進入不同的啟動區(qū)域,但是這段代碼無論無何都會被執(zhí)行,只不過是從哪里映射到起始地址而已。
等到整個U-Boot運行起來之后,接下來用戶就可以選擇是直接引導程序,還是進行內核更新或者文件系統(tǒng)更新,這些軟件的更新也是U-Boot提供的一些功能。
圖3 U-Boot通過串口打印出的CLI界面
單片機的Bootloader
既然Linux操作系統(tǒng)進行啟動時,必須要用Bootloader初始化硬件以及引導操作系統(tǒng),那么在沒有Linux操作系統(tǒng)的單片機中為什么還要用Bootloader?
單片機中使用Bootloader的主要作用有兩個:
改變軟件燒寫方式;
方便軟件更新(在線更新或者OTA)。
軟件更新這一點很好理解,因為目前很多設備都可以聯(lián)網,可以設置一個服務器自動推送一個新版本的固件來給單片機升級,既省去了客戶服務現場更新軟件的成本,又可以發(fā)揮軟件行業(yè)敏捷開發(fā)的優(yōu)點。
圖4 OTA升級
而改變軟件的燒寫方式這一點可能有點困惑了,現在的MCU明明可以用J-Link,用串口等等方式來燒寫,為什么還要去改變軟件的燒寫方式呢?
這個對于一些特殊的行業(yè),比如工程車輛,叉車,挖掘機等,它們既不支持OTA在線升級,對外的標準口也只有一個CAN總線接口,這個接口既要用作車輛信息診斷的接口,又要對整個車輛網絡中的所有控制器進行軟件升級。這就必然要給CAN總線加入軟件燒寫的功能,而單片機的出場IAP基本只支持串口,并不支持CAN總線等特殊通訊口。因此一個好的Bootloader程序可以完美解決這個問題。
圖5 林德叉車的CAN總線燒錄器(串口轉CAN)
下一篇文章,我們將來詳細地介紹下如何設計一個滿足OTA的STM32 Bootloader。