AUTOSAR BOOTLOADER
在汽车电子,现在bootloader基本已成汽车ECU必备,其负责应用程序的启动和通过CAN总线来更新应用程序和数据(如NVM)等。
一般而言,bootloader通过CAN总线来更新应用程序,有些也会通过LIN或者UART来更新程序,不管通过同种方式,都可以基于iso14229来实现,也就是说,iso14229 UDS协议算是一种上层应用协议,可用在多种通讯总线之上,而iso15765是基于CAN总线的UDS协议服务,其定义了如何通过CAN总线来传输大数据。
这里只讨论基于CAN总线的bootloader,说简单点,bootloader的主要功能是负责应用程序的启动和程序的更新,而更新过程由一系列的UDS服务组合起来实现的,主要由以下8条服务组成。OK,说到这里顺便提一下,目前已经有一个很成熟的bootloader规范叫HIS,其基于AUTOSAR,对一个bootloader该有的元素都做了很详细的定义,有兴趣的可以去了解下,看看他们的文档,不过最近不知道为什么,其官网访问不了,估计又被屏蔽了吧,哎,在党国不会翻墙,google会永远使用不了,百度还是个渣,郁闷。
##1 Session Control(会话控制 0x10)
##1.1 Request message definition
索引 | 参数名称 | 16进制值 |
---|---|---|
#1 | Diagnostic Session Control Request Serivce ID | 10 |
#2 | sub-function=[diagnostic session type] | 00-FF |
##1.2 Request message sub-function parameter $Level (LEV_) definition
16进制值(6位) | 描述 |
---|---|
01 | 默认会话层(Default Session) |
02 | 编程会话层(Programing Session) 这个就是bootloader主要要实现的一个会话层,在该会话层下要实现一系列的诊断服务,从而形成一个为升级应用程序提供服务的服务器,PC或手持客户机即可通过合法访问该会话层下的服务来实现应用程序的更新。 |
03 | 扩展会话层(Extended Session) |
#1.3 Positive response message
索引 | 参数名称 | 16进制值 |
---|---|---|
#1 | Diagnostic Session Control Response Serivce ID | 50 |
#2 | diagnostic session type | 00-7F |
#3 .. #n | sessionParameterRecord[] #1 = [data1 .. datan] | 00-FF |
一般而言,当ECU上电后,首先执行的是其类似于BIOS的一段程序,该程序一般由芯片厂商设计并不可改写,其会加载后续客户应用程序,对于汽车级ECU,其一般为bootloader,bootloader首先会检查是否有更新程序请求,如果没有,其将检查应用程序的有效性,如果有效,其将启动应用程序。在应用程序运行过程中,如果有客户机通过合法途径(如通过了安全访问)请求进入编程会话层,则应用程序会写一个标记,根据HIS规范,该标记会写到EEPROM里面,这样在应用程序执行复位操作之后,bootloader会去EEPROM里面读取该标记,即知有更新程序请求,从而停留在bootloader,启动编程服务器(Server),提供一系列的应用程序更新相关的诊断服务给客户机(Client)来实现程序的升级操作。但是,使用EEPROM来保存升级请求的方式,是开销比较大的,并且也使应用程序的启动时间变长,毕竟要去通过SPI/IIC等总线去EEPROM读取数据,即使是内部EEPROM数据,也会是比较慢的。所以一般会使用MCU内部在不掉电情况下,既使软件复位其值也不会丢失的内存或者寄存器来存储应用程序更新请求标记,相当的简单快捷。另外,在bootloader启动过程中,如果检测到应用程序无效,其也会停留在bootloader,启动服务器,等待客户机的连接并更新程序。
##2 Security Access(安全访问 0x27)
好吧,我又犯懒癌了,用MARKDOWN的HTML方式来写表格是一件好麻烦的事情,所以就不在描述消息的格式了。网上搜索iso14229下载其文档看吧。对于bootloader安全访问是必须的,从而保证应用程序不被非法客户机给破坏了。安全访问一般分为2步,第一步请求种子(Request Seed)和发送钥匙(Send Key)。一般,请求的种子为4字节,是由Server根据某种特殊算法随机生成,每次访问都会不一样,以防止恶意客户机找到规律给破解了。客户机收到种子之后,经过双方约定的算法来解密,算出钥匙,然后发送给服务器,如果钥匙有效则解锁成功,和此次解锁相关的服务就可以访问了。
目前,一般而言每个会话层就一个安全等级。但AUTOSAR DCM的描述不限于此,同一会话层下,可以有好多个安全等级,举例来说某些服务需要使用安全等级1的算法来解锁,又有其他一些服务需要安全等级2来访问,等等,这里要说每个等级没有高低之分,他们都是相等权重的,但等级0意味着不需要解锁,因为这是默认服务器的安全等级,该安全等级下的服务可以随意访问。
对于安全等级n,其访问数据格式如下:
-
客户机请求种子: [27,2n+1]
-
服务器返回种子: [67,2n+1,XX,XX,XX,XX]
-
客户机发送钥匙: [27,2n+2,YY,YY,YY,YY]
-
服务器解锁成功: [67,2n+2]
##3 Routine Control(过程控制 0x31)
在bootloader里过程控制31服务一般用于应用程序和非易失性数据NVM存储空的擦出操作。每一个Routine服务都有其唯一的标识符ID,其请求数据格式如下表所示:
索引 | 参数名称 | 16进制值 |
---|---|---|
#1 | RoutineControl Request Service Id | 31 |
#2 | sub-function = [ routineControlType: 01-startRoutine;02-stopRoutine;03-requestRoutineResults ] | 00-FF |
#3 #4 | routineIdentifier [] = [byte#1 (MSB) byte#2 ] | 00-FF |
#5 .. #n | routineControlOptionRecord [] = [OptionRecord#1 .. OptionRecord#m ] | 00-FF |
例如在我AS仓库里实现的bootloader, 标识符0xFF01用来请求应用程序存储空间Flash和非易失性数据NVM存储空间EEPROM的擦出操作,其格式如下:
索引 | 参数名称 | 16进制值 |
---|---|---|
#1 | RoutineControl Request Service Id | 31 |
#2 | sub-function = [ routineControlType: 01-startRoutine,我在实现时只支持start,即得到肯定回复时,必然擦除成功 ] | 00-FF |
#3 #4 | routineIdentifier [] = [FF 01 ] | 00-FF |
#5 #6 #7 #8 | address [ byte#1 (MSB) byte#2 byte#3 byte#4 ] | 00-FF |
#9 #10 #11 #12 | length [ byte#1 (MSB) byte#2 byte#3 byte#4 ] | 00-FF |
#13 | 内存标识: 0xFF-Flash;0xEE-EEPROM | 00-FF |
##4 Request Download(请求下载 0x34)
在bootloader里请求下载服务用于告诉服务器,客户机即将下载一段程序或者数据到客户机,对于我在AS仓库的一个实现,其请求数据格式如下:
索引 | 参数名称 | 16进制值 |
---|---|---|
#1 | RequestDownload Request Service Id | 34 |
#2 | dataFormatIdentifier:我使用0x00,记没有任何加密以及压缩 | 00-FF |
#3 | addressAndLengthFormatIdentifier:我使用0x44,即地址和长度信息都为4个字节 | 00-FF |
#4 #5 #6 #7 | address [ byte#1 (MSB) byte#2 byte#3 byte#4 ] | 00-FF |
#8 #9 #10 #11 | length [ byte#1 (MSB) byte#2 byte#3 byte#4 ] | 00-FF |
#12 | 内存标识: 0xFF-Flash;0xEE-EEPROM;0xFD-Flash Driver | 00-FF |
如上表中第12个字节参数内存标识是我添加的,由于ARCCORE v3.1源代码并没有实现34/35/36/37服务,所以我在其代码的基础上增加了这些服务,为使代码简单但并复用其DCM memory管理机制,我选择使用内存标识的方法来区分不同内存快,而不是使用地址空间来区分,虽然和标准iso14229有些许的不一致,但也无妨,我这里只为基于AUTOSAR实现一个简单的示例bootloader,可用我开发的easySAR图形配置工具打开文件boot/common/autosar.arxml来了解所有关于bootloader诊断的配置信息。
##5 Request Upload(请求上载 0x35) 该服务和下载服务差不多,唯一的不同是其是为了请求从服务器Server端读取应用程序或者非易失性数据NVM到客户机,对于我在AS仓库的一个实现,其请求数据格式如下,基本和下载服务一样:
索引 | 参数名称 | 16进制值 |
---|---|---|
#1 | RequestUpload Request Service Id | 35 |
#2 | dataFormatIdentifier:我使用0x00,记没有任何加密以及压缩 | 00-FF |
#3 | addressAndLengthFormatIdentifier:我使用0x44,即地址和长度信息都为4个字节 | 00-FF |
#4 #5 #6 #7 | address [ byte#1 (MSB) byte#2 byte#3 byte#4 ] | 00-FF |
#8 #9 #10 #11 | length [ byte#1 (MSB) byte#2 byte#3 byte#4 ] | 00-FF |
#12 | 内存标识: 0xFF-Flash;0xEE-EEPROM;0xFD-Flash Driver | 00-FF |
##6 Transfer Data(传输数据 0x36)
数据传输服务则为实现应用程序数据由客户机到服务器或者由服务器到客户机的传输过程,取决于客户机请求的是何种传输模式,下载(34)模式还是上载(35)模式,如果是下载,其客户机请求服务格式如下:
索引 | 参数名称 | 16进制值 |
---|---|---|
#1 | TransferData Request Service Id | 36 |
#2 | blockSequenceCounter | 00-FF |
#3 | reserved | 00 |
#4 | 内存标识: 0xFF-Flash;0xEE-EEPROM;0xFD-Flash Driver | 00-FF |
#5 ... #n | data [ byte#1 ... byte#m ] | 00-FF |
由上表所示,为了简化bootloader程序并保证数据data的起始地址能够4字节对齐,所以我增加了参数#3和#4。当服务器正确响应请求并将数据写入相应内存之后,服务器会给出如下响应:
索引 | 参数名称 | 16进制值 |
---|---|---|
#1 | TransferData Response Service Id | 76 |
#2 | blockSequenceCounter | 00-FF |
如果是上载数据,其客户机请求格式如下表所示:
索引 | 参数名称 | 16进制值 |
---|---|---|
#1 | TransferData Request Service Id | 36 |
#2 | blockSequenceCounter | 00-FF |
#3 | reserved | 00 |
#4 | 内存标识: 0xFF-Flash;0xEE-EEPROM;0xFD-Flash Driver | 00-FF |
当服务器正确响应请求并读取相应数据之后,服务器会给出如下响应:
索引 | 参数名称 | 16进制值 |
---|---|---|
#1 | TransferData Response Service Id | 76 |
#2 | blockSequenceCounter | 00-FF |
#3 ... #n | data [ byte#1 ... byte#m ] | 00-FF |
好吧,个人认为还是相当简单的,基本就是一问一答,按照固定的模式将下载的数据写入存储空间或者将将相应数据读取出来返回给客户机。
##7 Request Transfer Exit(请求传输结束 0x37)
见名知意,请求一个传输过程的结束。即是说一个完整的过程分为三步,第一步是请求上载或者下载,第二步不断的数据传输直至结束,第三步请求传输结束。
##8 Ecu Reset(ECU复位 0x11)
呵呵,这个更简单了,请求ECU复位,当应用程序更新完完毕时,重启以启动更新后的应用程序。
Okay,以上介绍了一些bootloader关键性的诊断服务,当然还会有一些其他服务如什么写Finger Print啊,应用程序完整性校验啊什么的,但我就不管了,只要掌握思想原理,随你客户需求怎么提,I don't care,一切都只是时间和工作量的问题。下面就简单将上述服务串起来,讲述一个完整的bootloader应用程序升级过程。
1 当前应用程序正在执行,客户机经过安全访问之后,取得了进入program session的权限,然后请求进入program session,之后应用程序写一个程序更新请求标识到NVM或者软件复位其值不会丢失的内存RAM或者寄存器中,并执行软件复位(可利用开门狗来复位)。
2 bootloader开始执行,检测到程序更新请求标识,启动Server,等待客户机后续响应。
3 客户机通过安全访问Security Access,获取其他被保护服务的访问权限,解锁成功。
4 客户机请求下载Flash Driver到可执行RAM空间。这里说明下,出于安全行考虑,一般bootloader的Flash驱动都会通过动态下载的方式。当然,有些bootloader会选择内联Flash Driver,这样,这一步操作就可以省略了。内联Flash Driver的隐患是当程序一旦跑飞,并执行了Flash Driver的API可能会破坏应用程序和bootloader使设备变砖。
5 客户机请求擦除应用程序存储空间以来更新程序。
6 客户机下载应用程序。
7 客户机执行ECU Reset,升级完成。
看吧,还是很简单的。