前
最近, follow 一个开源项目, BusPirate ,总线海盗,一个很棒的超多功能集一身的一个 tool. 对其中的 bootloader 印象深刻, 虽然不是第一次知道这东西, 这次也是来了兴致. 学习一下.
- BusPirate Github
- BusPirate Offical Website
What
先看看词条的定义:
在嵌入式操作系统中,BootLoader是在操作系统内核运行之前运行。可以初始化硬件设备、建立内存空间映射图,从而将系统的软硬件环境带到一个合适状态,以便为最终调用操作系统内核准备好正确的环境。在嵌入式系统中,通常并没有像BIOS那样的固件程序(注,有的嵌入式CPU也会内嵌一段短小的启动程序),因此整个系统的加载启动任务就完全由BootLoader来完成。在一个基于ARM7TDMI core的嵌入式系统中,系统在上电或复位时通常都从地址0x00000000处开始执行,而在这个地址处安排的通常就是系统的BootLoader程序。
实际上我们简单讲, 就是在嵌入式设备中, 给我们即将执行的程序(系统), 提供运行初始化环境的东西, 在初始化完成之后, 直接进行 jmp 使得PC 到目标程序空间, 永不返回(一般性).
这里,就是以这个项目提供的bootloader源码进行.
How
文件定位: https://github.com
本想着可能是使用 C 写的, 发现除了配置文件, 只有 .s 汇编了,
文件前段,是以下内容注释:
ds30 Loader is free software: you can redistribute it and/or modify
;
it under the terms of the GNU General Public License as published by the Free Software Foundation.
这个 bootloader 应该是,第三方所开发的框架, 直接对其进行修改就好
静态初始化
;------------------------------------------------------------------------------; Register usage;------------------------------------------------------------------------------ ;.equ MIXED, W0 ;immediate .equ DOERASE, W1 ;flag indicated erase should be done before next write .equ WBUFPTR, W2 ;buffer pointer .equ WCNT, W3 ;loop counter .equ WADDR2, W4 ;memory pointer .equ WADDR, W5 ;memory pointer .equ PPSTEMP1, W6 ;used to restore pps register .equ PPSTEMP2, W7 ;used to restore pps register .equ WFWJUMP, W8 ;did we jump here from the firmware? ;.equ UNUSED, W9 ; .equ WDEL1, W10 ;delay outer .equ WDEL2, W11 ;delay inner ;.equ UNUSED, W12 ; .equ WCMD, W13 ;command .equ WCRC, W14 ;checksum .equ WSTPTR, W15 ;stack pointer
这里的一段代码来自文件首,,equ
的伪指令也说明了 这里是,寄存器的宏定义,给不同寄存器,起别名, 保证了程序易读性.
下面导入, 单片机设置. 和我们平时使用的 #include <reg52.h>
类似
;------------------------------------------------------------------------------; Includes;------------------------------------------------------------------------------.include "settings.inc"
在这段代码的注释部分已经说明, 这里是 constants的 ,不需要进行修改. 主要内容主要是, 对于 将会使用的常量的宏. 内容有 字符, 延时, 串口波特率, 页大小, 和 STARTADDR . 其原文的注释,对这些 静态符号也有很好的说明
;------------------------------------------------------------------------------; Constants, don't change;------------------------------------------------------------------------------ .equ VERMAJ, 1 /*firmware version major*/ .equ VERMIN, 0 /*fimrware version minor*/ .equ VERREV, 2 /*firmware version revision*/ .equ HELLO, 0xC1 .equ OK, 'K' /*erase/write ok*/ .equ CHECKSUMERR,'N' /*checksum error*/ .equ VERFAIL, 'V' /*verification failed*/ .equ BLPROT, 'P' /*bl protection tripped*/
.equ BLDELAY, ( BLTIME (FCY / 1000) / (65536 7) ) /delay berfore user application is loaded/
;.equ UARTBR, ( (((FCY / BAUDRATE) / 8) – 1) / 2 ) /baudrate/
/ issue 11 in errata for A3, optimal value causes reception to fail /
/ autocalculate: 0x21, <2.5% error /
/ working: 0x22, <3% error, same as main firmware /
.equ UARTBR, 0x22;((FCY/(4BAUDRATE))-1)
.equ PAGESIZE, 512 /words/
.equ ROWSIZE, 64 /words/
; 这个 指令是注释掉了的,
; .equ STARTADDR, ( FLASHSIZE – 2(PAGESIZE 2) ) /place bootloader in 2nd last program page/
.equ STARTADDR, ( FLASHSIZE – (2 (PAGESIZE)) ) /place bootloader in last program page/
.equ BLCHECKST, ( STARTADDR – (ROWSIZE) ) /precalculate the first row write position that would overwrite the bootloader/
.equ BLVERSION, 0x0405 ;bootloader version for Bus Pirate firmware (located at last instruction before flash config words)
对 一些 宏的 定义值进行合法性检测.
;------------------------------------------------------------------------------; Validate user settings;------------------------------------------------------------------------------ ; Internal cycle clock .if FCY > 16000000 .error "Fcy specified is out of range" .endif ; Baudrate error .equ REALBR, ( FCY / (4 * (UARTBR+1)) ) .equ BAUDERR, ( (1000 * ( BAUDRATE - REALBR)) / BAUDRATE ) .if ( BAUDERR > 30) || (BAUDERR < -30 ) .error "Baudrate error is more than 3%. Remove this check or try another baudrate and/or clockspeed." .endif .if BLDELAY<1 .error "Bootloader delay is 0".endif ...
这里,在数据段分配空间 , 存放固件签名地址
;------------------------------------------------------------------------------; Uninitialized variables in data memory;------------------------------------------------------------------------------ .bssbuffer: .space ( ROWSIZE * 3 + 1/*checksum*/ ) .equ FIRMWARE_SIGNATURE_LOW, 0x3141 .equ FIRMWARE_SIGNATURE_HIGH, 0x5926 .global skip_pgc_pgd_check .global firmware_signature .section *, bss, address(0x27FA)skip_pgc_pgd_check: .space 2firmware_signature: .space 4
签名校验
在文件的前面, 有声明一个全局符号
1 |
;------------------------------------------------------------------------------ |
由符号可以直接看出, 是一个复位向量, 下面是标号内容, 也就是我们一上电会执行的东西
1 |
;------------------------------------------------------------------------------ |
1 |
;------------------------------------------------------------------------------ |
上面的这段代码, 实际上应该是对,当前的单片机内是否有 有效的固件 进行检测. 固件包含签名, 有签名就是有固件.
跳线检测
1 |
jumper_test: |
结合实际上的具体操作, 上面的这部分就是比较容易理解了.
实际上, 在给板子使用 bootloader 进行固件烧写的时候,的确需要一个 jumper 连接 pgc 和 pgd 这两个脚. 否则的话, 显示如下err内容,
1 |
+++++++++++++++++++++++++++++++++++++++++++ |
上述的代码, 就是对上电时候是否有进行 跳线进行检测, 从而执行不同的后续操作.
- Firmware Upgrade – dangerousprototypes
用户程序 UserApp
在上面的bootloader , bra 到quit 标号的时候, 惊奇的发现,后面就开始执行我们的用户程序了.
1 |
quit:;clean up from jumper test |
可是, 上面的 setup 必须是要有个 jumper 多麻烦, 这里的 G1k精神所在, 所以, 会有以下的标号段.
1 |
;------------------------------------------------------------------------------ |
这个符号被导出, 我们的用户程序中, 可以进行一次跳转. 回到我们的 bootloader.
这里 也找到在固件中存在的 跳转部分
- 源文件链接 ProcMenu.c:666
C 代码如下
1 |
// ProcMenu.c |
串口初始化 UART
在前面的检测中, 如果jumper是存在的, 这里就进行 setup操作, 这里的第一步就是配置 硬件串口
1 |
setup: |
完成了配置之后, 进行初始化
1 |
;------------------------------------------------------------------------------ |
设备握手
这里的设备握手, 是直接在 串口的后面执行的.
1 |
;------------------------------------------------------------------------------ |
串口内容的单字节接收函数, 用于握手识别
1 |
;------------------------------------------------------------------------------ |
串口数据发送宏, 实现单字节数据发送.
1 |
;------------------------------------------------------------------------------ |
初始化
1 |
; Send ok |
数据接收
这里就是开始接收 数据了
1 |
;---------------------------------------------------------------------- |
烧写准备
这里的部分, 是把数据真正的烧入 flash 前的检验工作, 确保 我写入的数据是不会影响到我 Bl 的本身的空间的, 如果影响到自己, 把自己抹掉了不就是尴尬了.
检查是否数据容量是否超出, 导致覆盖, 这里保留官方的注释
1 |
;---------------------------------------------------------------------- |
指针初始化
1 |
;---------------------------------------------------------------------- |
烧写 Flash
这一部分, 就开始由 BL 实现对 Flash 的烧写了, 首先对flash 进行擦出.
1 |
;---------------------------------------------------------------------- |
对Flash 进行擦出之后, 就可以开始我们的烧写过程了
1 |
;---------------------------------------------------------------------- |
这里, 是写命令的实现部分, 突然发现,这里用的应该是一个硬件控制器,实现的对,flash的控制
1 |
;------------------------------------------------------------------------------ |
对于烧写部分的后续过程, 还有, 内容校验, 和错误处理的过程.
;---------------------------------------------------------------------- ; Verify row;----------------------------------------------------------------------;----------------------------------------------------------------------; Verify fail;----------------------------------------------------------------------
总结
通过这个源码, 算是对简单的Bootloader 有了简单的了解, 在 Bp 的这个 bootloader里面, 显示通过检测 调试开关 , 决定是否进入调试状态. 之后进行串口初始化. 完成之后进行串口握手以确认设备, 即版本, 随后开始进行数据接收, 把其存放在 一个 缓冲区内, 之后通过 NVM 控制器, 对Flash进行烧写, 完成烧写过程.