ARMv8总体启动流程

转自:https://zhuanlan.zhihu.com/p/519995589

聊聊SOC启动 armv8启动总体流程

1 为什么需要引导程序

  在遥远的单片机时代,嵌入式设备功能比较单一,每个设备只需要执行一件简单的任务,因此在系统初始化完成后,程序就运行在一个大循环中,此时,系统启动流程和功能代码并没有很严格的区分。
  随着CPU处理能力和芯片制造工艺的不断提升,高性能芯片逐渐演进成了SOC,单颗芯片上除了CPU外还会集成大量不同功能的IP核,如中断控制器、DSP、GPU、总线控制器、视频处理和编码模块、以及电源管理模块等。为了更好地管理这些软硬件资源,以及为上层用户提供统一的服务和接口,SOC上一般都需要运行操作系统。系统引导程序主要功能就是初始化系统的软硬件状态,为操作系统提供一个合适的软硬件环境,以及加载和启动操作系统映像。

2 软硬件假定
  为了更好地描述启动流程的细节,后面的章节我们将选取一种主流的硬件平台和软件框架做讨论。硬件平台选用qemu的virt machine平台,其主要硬件配置如下:
(1)cpu为armv8架构的cortex-a53核
(2)中断控制器为gicv3
(3)ROM空间128k
(4)SRAM空间384k
(5)DDR内存空间3G
(6)支持semihosting半主机模式
  选用qemu是因为其是一种硬件模拟器,可以通过软件模拟的方式实现不同硬件平台的功能,如可以在X86主机上模拟armv8架构的硬件,从而可以在不需要target硬件的情况下进行相关功能的调试。qemu调试环境的搭建可参考以下的链接:
其软件架构如下:
(1)初始启动流程使用arm ATF
(2)内核启动流程使用uboot
(3)内核为linux

3 启动阶段

  Armv8的启动流程包含多个阶段,典型地有BL1、BL2、BL31、BL32、BL33,根据需求的不同,这些阶段可以适当地裁剪或添加。为了方便描述,后面我们的讨论将基于以上这些官方定义的标准阶段,它们的源码会被编译成独立的启动镜像,并被保存到特定的存储介质中。由于一般的存储介质(如spi flash、nand flash、emmc、ssd等)都不支持代码的直接执行,因此需要在启动时先将镜像加载到可直接执行代码的存储介质,如SRAM或DDR中,然后运行相关代码。其典型的加载流程如下:

ARMv8总体启动流程_第1张图片
ARMv8总体启动流程_第2张图片
  1. BL1是启动的第一阶段,该镜像必须要存储在可直接执行的介质中。若芯片支持XIP启动方式,其可被存储在片外可直接执行的介质中(如norflash)。若不支持XIP,则需要存储在芯片的片内ROM中,此时在芯片出厂后该部分代码就将被固化,后续再也不能被修改和升级。若芯片要支持安全启动,则需要将bootrom作为启动时的信任根,此时除调试阶段外,SOC必须禁用XIP。关于安全启动我们将在后面专门介绍
  2. BL2镜像由BL1加载,此时DDR还没有被初始化,因此它需要被加载到片内的SRAM中执行,一般在这个阶段会完成DDR的初始化,因此后面的镜像都可以被加载到DDR中。从上图可知,BL31、BL32和BL33都是由BL2加载的,其中BL31和BL32是可选的,若系统不支持TRUST OS,则可去掉BL32,若不支持EL3异常等级及secure monitor,则可去掉BL31
  3. BL33一般指uboot,一般通过它最终启动操作系统内核
    Armv8架构典型的启动流程如下:
ARMv8总体启动流程_第3张图片
以上流程中我们假定系统支持的最高异常等级为EL3,且支持secure monitor和TRUST OS,同时BL2运行在secure EL1,BL33运行在non secure EL1或non secure EL2状态
(1)由于armv8架构规定,arm核复位后默认会进入当前系统支持的最高异常等级,因此BL1运行在EL3,它执行完成后会通过异常返回ERET的方式跳转到BL2
(2 - 3)BL2执行完成后需要跳转到BL31,由于BL31运行在EL3异常等级,而BL2根据需求不同可能运行于secure EL1或EL3。当BL2运行于EL3时可直接通过ERET方式跳转到BL31中,但若其运行在secure EL1时,则只能通过smc异常触发进入EL3异常等级。
  显然,此时BL31由于尚未设置其自身的smc异常处理程序而无法直接处理该异常,因此,为了完成跳转流程,BL1需要先代理该异常的处理。因此BL1在退出之前先设置smc异常处理函数,BL2触发smc启动BL31时,BL1捕获该异常并根据BL2传入的参数设置BL31的入口地址和系统初始状态,并通过ERET跳转到BL31的入口地址处执行
(4)BL32阶段会运行TRUST OS,它运行于secure EL1异常等级,BL31可根据其镜像加载信息设置入口地址以及其它状态,并完成跳转
(5 - 6)BL32加载完成后将通过SMC返回到BL31,然后由BL31跳转到non secure EL1或non secure EL2以执行BL33

4 重要寄存器介绍

  除了硬件默认设置的寄存器值之外,其它寄存器都需要在系统启动时初始化,有些重要的寄存器在系统启动流程中会影响系统的运行行为,因此下面对它们做一简单介绍。 由于armv8很多寄存器都为不同异常等级提供了对应的bank定义,而其含义比较类似,为了方便介绍,后面介绍中我们都以EL3等级下的定义为例

4.1 SCTLR_EL3寄存器

  该寄存器是系统控制寄存器,用于提供系统顶级的状态控制信息,其定义如下图:

ARMv8总体启动流程_第4张图片
其每个bit的含义可参考armv8参考手册,下面简单介绍一些该寄存器的重要控制位:
(1)M[0]:用于设置系统是否使能EL3下的MMU,若其为1使能MMU,否则禁止MMU
(2)A[1]:用于设置是否使能El3下的对齐检查,若其为1使能对齐检查,否则进制对齐检查
(3)C[2]:用于设置EL3下的数据cache,若其为1使能数据cache,否则禁止数据cache
(4)I[12]:用于设置EL3下的指令cache,若其为1使能指令cache,否则禁止指令cache
(5)WXN[19]:用于设置EL3下写权限内存是否不可执行,若其为1则含有写权限的内存不具有执行权限,否则没有副作用
(6)EE[25]:用于设置EL3的大小端,若其为0数据为小端格式,否则为大端格式.

4.2 SCR_EL3寄存器

  该寄存器用于设置secure相关的属性,且在其它异常等级下不存在bank值,而是用于控制全局状态的。 因此在不同异常等级切换之前,需要先将该寄存器的值设置为目标异常等级所需的值,如从EL3切换到non secure EL1,则需要将其设置为non secure EL1相关的配置。寄存器的定义如下:

ARMv8总体启动流程_第5张图片

其重要的bit位如下:
(1)NS[0]:设置EL0 - EL2的secure状态,若其为0表示secure状态,为1表示non secure状态
(2)IRQ[1]:用于设置irq中断路由,当其为1时,irq中断都被路由到EL3处理,当其为0时,若当前运行在EL3下irq中断不触发,否则irq中断路由到当前运行的异常等级,而不路由到EL3
(3)FIQ[2]:用于设置fiq中断路由,当其为1时,fiq中断都被路由到EL3处理,当其为0时,若当前运行在EL3下fiq中断不触发,否则fiq中断路由到当前运行的异常等级,而不路由到EL3
(4)EA[3]:用于设置外部abort和serror路由,当其为1时,外部abort和serror都被路由到EL3处理,当其为0时,若当前运行在EL3下serror不触发,外部abort路由到EL3,否则外部abort和seeror都路由到当前运行的异常等级,而不路由到EL3
(5)SMD[7]:用于设置是否禁用smc异常,若其为1禁用smc,否则使能smc
(6)HCE[8]:用于设置是否禁用hvc异常,若其为1使能hvc,否则禁用hvc
(7)ST[11]:用于设置secure EL1是否将counter的secure timer寄存器访问路由到EL3。若其为1,secure EL1访问counter的secure timer寄存器将会路由到EL3,否则该bit被忽略
(8)TWI[12]:用于设置EL0 – EL2执行WFI时是否陷入EL3。若其为1则任何WFI操作都会陷入EL3,否则该bit被忽略
(9)TWE[13]:用于设置EL0 – EL2执行WFE时是否陷入EL3。若其为1则任何WFE操作都会陷入EL3,否则该bit被忽略

4.3 SP_EL0和SP_EL3寄存器

  Armv8包含SP_EL0和SP_ELx两种栈指针寄存器,其中SP_ELx包含SP_EL1 – SP_EL3三个bank寄存器。SP_ELx寄存器为异常栈指针寄存器,即cpu进入异常后将会切到捕获该异常对应EL的SP_ELx寄存器,如陷入smc异常时栈指针寄存器将会切换到SP_ELx。SP_EL0可以被任何异常等级使用,因此在EL1 – EL3中除了异常处理流程外,执行其它代码逻辑时一般都会切换为SP_EL0栈指针。

4.4 SPSR_EL3和ELR_EL3寄存器

  通过中断或异常陷入EL3时,陷入异常之前的PSTATE寄存器将会被保存到SPSR_EL3寄存器中,当异常处理完成后通过ERET指令返回断点处继续执行之前,将用该寄存器的值恢复PSTATE的值。ELR_EL3与SPSR_EL3类似,指示在异常陷入之前该寄存器将用于保存返回地址,异常处理完成后则将该返回地址恢复到PC中。

5 镜像跳转方式

  前面我们聊到镜像跳转时可能需要使用到ERET或SMC指令,下面我们再看下跳转时的细节。

5.1 Smc异常处理流程

  smc是armv8支持的异常跳转指令,它用于程序从EL1或EL2跳转到EL3异常等级。其跳转流程如下:

ARMv8总体启动流程_第6张图片
以atf的BL1 smc处理流程为例,EL1调用者通过smc指令陷入到EL3异常,此后CPU将跳转到EL3异常向量表的vector_entry SynchronousExceptionA64入口处,该函数解析esr_el3寄存器的异常原因,若其为smc异常,就跳转到smc处理函数smc_handler64中。smc_handler64解析通过x0 – x7寄存器传入的参数获取smc命令类型,执行特定的命令处理函数,并通过寄存器x0 – x4返回处理结果。对于异常向量表跳转入口选择原则可参考以下博文。

5.2 ERET跳转流程

  ERET指令用于从异常处理流程中返回,在介绍异常返回流程之前,我们先看一下armv8异常跳转的流程。Armv8触发异常或中断后硬件将会执行以下操作:
(1)将PSTATE寄存器的内容保存到SPSR_ELx中,其中x表示异常进入的异常等级,如smc异常将会陷入EL3,因此相应的寄存器就为SPSR_EL3
(2)将异常处理完成后需要返回的地址保存在ELR_ELx中
(3)设置中断和异常掩码DAIF,以关闭所有中断和异常
(4)若异常是同步异常或SError中断,异常状态信息将被保存在ESR_ELx中,该信息可用于分析异常发生的原因
(5)若异常为指令异常、数据异常或对齐错误等,则触发异常对应的内存地址将被保存到FAR_ELx寄存器中
(6)栈指针切换为目标异常等级的栈指针寄存器SP_ELx
(7)程序跳转到对应的异常处理入口,执行异常处理程序
  在异常执行完成后,可通过ERET指令返回被异常中断程序的断点处继续执行,该指令将使硬件执行以下操作:
(1)用SPSR_ELx寄存器的内容恢复PSTATE寄存器
(2)用ELR_ELx寄存器的内容恢复PC值
  从以上流程可以看到,执行ERET指令后程序的执行流将由SPSR_ELx和ELR_ELx决定。因此,我们在执行镜像之间的跳转,只要在ERET之前将ELR_ELx设置为待跳转镜像的入口地址,并设置正确的SPSR_ELx即可完成

6 内存规划

6.1 内存规划原则

  嵌入式系统的内存一般包含ROM、SRAM和DDR,其中ROM和SRAM位于SOC片内,DDR位于芯片外部。它们的特点如下:
(1)ROM中的内容断电后不会消失,不仅可用于代码执行,还可以用于镜像存储,但其只有只读权限
(2)SRAM和DDR中的内容在断电后都会消失,因此只能被用于代码的动态执行,而不能用于镜像存储
(3)ROM和SRAM都是直接连接总线上,系统上电后即可直接执行。而DDR需要通过DDR phy和DDR controller连接到总线上,因此使用之前必须要先对其执行初始化操作
  根据上述各种内存的特点和前面镜像加载启动流程的需求,在内存规划中我们需要考虑以下几个问题:
(1)由于BL1需要固化到ROM中,且是系统最先执行的,因此ROM地址需要被映射到cpu的重启地址处
(2)由于ROM是只读的,BL1镜像除了代码段和只读数据段之外还包含可读写数据段,这部分数据在BL1启动时需要从ROM重定位到SRAM中
(3)由于BL1被固化在ROM中,芯片出产后就不能更改,因此DDR初始化代码不能集成到BL1中。故BL2需要被加载到SRAM中执行,且在BL2中执行DDR初始化流程
(4)BL2之后的其它镜像既可以运行于SRAM中,也可以运行于DDR中
(5)从前面的镜像启动流程可知,若BL2运行于secure EL1下,当其执行完成后,需要通过smc再次陷入BL1去执行BL31流程,因此BL2和BL1的地址不能有重叠
(6)BL31除了执行启动流程外,在系统运行过程中还会以secure monitor的方式驻留,为normal空间的smc异常提供服务历程,以及为normal os和trust os之间提供消息转发、中断路由转发等功能。因此,BL31镜像需要永久驻留内存,在系统启动完成后不能被回收
(7)与BL31类似,BL32在启动后需要驻留内存为系统提供安全相关服务,因此为其所分配的内存也不能被回收(8)除此之外,BL1、BL2和BL33(一般为uboot)的内存在系统启动完成后都可以被释放给操作系统使用

6.2 qemu virt平台内存规划示例

根据以上内存规划原则,qemu virt machine各启动阶段的内存规划如下:

类型 起始地址 结束地址 长度 是否secure内存 作用
ROM 0x00000000 0x00020000 128k Yes BL1(bootrom)
SRAM 0x0e04e000 0x0e060000 72k Yes Bl1(rw data)
SRAM 0x0e000000 0x0e001000 4k Yes Shared ram
SRAM 0x0e01b000 0x0e040000 148k Yes BL2
SRAM 0x0e040000 0x0e060000 128k Yes BL31
SRAM 0x0e001000 0x0e040000 252k YES BL32
DDR 0x60000000 0x100000000 2.5G NO BL33

你可能感兴趣的:(ARM,嵌入式硬件)