STM32MP157A-DK1测评 (5) STM32MP Linux系统启动过程
[复制链接]
本帖最后由 cruelfox 于 2020-4-26 11:53 编辑
如果把Linux操作系统认为是一个包含很多模块的程序的话,这个程序(被叫做Linux内核)是可以以单一文件形式存在的,可以像普通文件那样拷贝。不过,就像如 x86 PC Linux,这个程序(常见为 vmlinux, vmlinuz, bzImage ... 等文件名)并不是拷贝到硬盘上就可以启动电脑那样,嵌入式 Linux (常见的如ARM, MIPS平台) 的这样一个文件也不是写到 Flash 存储器中就可以运行起来。
在 PC 上,操作系统的引导(boot)过程有几个步骤,首先是ROM (BIOS,以及现在的UEFI) 程序会去识别硬盘上的主引导记录,将主引导记录里面的一段代码读到RAM里面运行,然后这段代码去查找可引导的硬盘分区,读取分区中的引导代码来运行,再接下来是分区中的引导代码查找操作系统的文件…… 系统越复杂环节越多,这样设计才能灵活应对多变的软硬件环境。
PC 上 Linux 的引导程序最广泛的叫 GRUB, 它的最主要作用是把 Linux 从硬盘上读到内存中,并传递运行参数。在 ARM 平台,通常是用 U-Boot 这个程序来引导 Linux 的,这是一个开源软件,支持很多样的硬件。如果 ARM MPU或MCU上电复位以后自动从某个地址开始执行代码的话,就应当把 U-Boot 的代码放在那里。
不过对于 STM32MP157, 情况还不是这么简单。首先这个 MPU 里面没有 Flash, 不能把 U-Boot 烧写进去;它也不是从片外总线上访问 NOR Flash 直接执行。STM32MP157 上电执行的是自己的 ROM 程序,由 ROM 程序从其它介质(比如现在用的 SD 卡) 中读取代码到 SRAM,或者从 USB、UART 获取代码存到 SRAM,还要经过它监督核查以后,才可以执行。为什么搞复杂了呢?因为是引入了安全机制。 对于许多不需要所谓安全的应用来说,虽然可以不用,毕竟又增添了 MCU 到 MPU 跨越的难度。
用 wiki 上的图,这里 Linux 系统启动步骤以及运行时的环境可能是这样的:
ROM 程序首先从 SD 卡读取的是叫做 TF-A 的 FSBL (第一阶段bootloader),而 U-Boot 作为 SSBL (第二阶段bootloader) 需要由 TF-A 来装入。后面才是 U-Boot 读取 Linux 内核程序。上图左边的一列表示为“安全”的环境。至于 OP-TEE,它是在 Linux 系统之外的一个服务,是可选的。
作为 MPU 入门学习,就忽略“安全”的部分吧。值得留意的是,TF-A 是在 STM32MP157 片上的 SYSRAM 中执行的,而 U-Boot 是在片外的 DDR SDRAM 中执行的。
TF-A, OP-TEE, U-Boot 以及 Linux(内核) ST都提供了源代码包和ST的patch. 想折腾的话有相当多的内容可以折腾。
关于 STM32MP157A-DK1 的Flash(SD卡)资源排布,官方包中有 4 种配置文件:
FlashLayout_sdcard_stm32mp157a-dk1-trusted.tsv
FlashLayout_sdcard_stm32mp157a-dk1-basic.tsv
FlashLayout_sdcard_stm32mp157a-dk1-extensible.tsv
FlashLayout_sdcard_stm32mp157a-dk1-optee.tsv
里面描述了什么镜像文件要写到什么位置,这里面包括了文件系统分区的镜像描述。
文件名中有 trusted 的就对应前面那一个图,里面重点是这几行:
P 0x04 fsbl1 Binary mmc0 0x00004400 tf-a-stm32mp157a-dk1-trusted.stm32
P 0x05 fsbl2 Binary mmc0 0x00044400 tf-a-stm32mp157a-dk1-trusted.stm32
P 0x06 ssbl Binary mmc0 0x00084400 u-boot-stm32mp157a-dk1-trusted.stm32
P 0x21 bootfs System mmc0 0x00284400 st-image-bootfs-openstlinux-weston-stm32mp1.ext4
其中 FSBL 实际上存了两份(不同位置,后面是同一个文件,我从SD卡读出来比较过也是一样的),SSBL 只存了一份。
如果不想要"trusted",可以用 basic 那个配置,也就是我第一次写卡用的。里面描述的文件就改变了:
P 0x04 fsbl1 Binary mmc0 0x00004400 u-boot-spl.stm32-stm32mp157a-dk1-basic
P 0x05 fsbl2 Binary mmc0 0x00044400 u-boot-spl.stm32-stm32mp157a-dk1-basic
P 0x06 ssbl Binary mmc0 0x00084400 u-boot-stm32mp157a-dk1-basic.img
P 0x21 bootfs System mmc0 0x00284400 st-image-bootfs-openstlinux-weston-stm32mp1.ext4
没有了 "trusted" 的 u-boot, 分成了两个部分,多了一个 u-boot-spl.
为什么还一定要分成 FSBL, SSBL 两步呢?因为这两部分运行环境不同,FSBL 要做一些 ROM bootloader 没有做的初始化工作。至少,SDRAM 是由 FSBL 初始化的,而 U-Boot 需要在 SDRAM 中运行。
我试着把 SD 卡中 SSBL 部分的内容清除掉——全写成 0xFF 字节。这样就只能执行到 FSBL 阶段会停止,看看是什么效果?
果然是在试图读取 SSBL ,卡住了。
打开 OpenOCD 调试:
Open On-Chip Debugger
> halt
stm32mp15x.cpu0 rev 5, partnum c07, arch f, variant 0, implementor 41
stm32mp15x.cpu0 cluster 0 core 0 multi core
stm32mp15x.cpu1 rev 5, partnum c07, arch f, variant 0, implementor 41
stm32mp15x.cpu1 cluster 0 core 1 multi core
target halted in Thumb state due to debug-request, current mode: Supervisor
cpsr: 0x800001f3 pc: 0x0000060e
MMU: disabled, D-Cache: disabled, I-Cache: enabled
target halted in ARM state due to debug-request, current mode: Undefined instruction
cpsr: 0x600001db pc: 0x2ffc2540
MMU: disabled, D-Cache: disabled, I-Cache: enabled
发现异常状态:非法指令不能执行。
> reg pc
pc (/32): 0x2FFC2540
> arm disassemble 0x2FFC2540 2
0x2ffc2540 0xeafffffe B 0x2ffc2540
0x2ffc2544 0xea00002a B 0x2ffc25f4
这样确认 CPU 处于异常处理程序中(死循环)。根据 LR 寄存器的值,可以得知发生异常的地址(注意,Cortex-m CPU没有这个特性),故查看 LR 寄存器:
> reg lr
lr (/32): 0xC0100004
> mdw 0xc0100000 8
0xc0100000: ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff
貌似已把我擦掉的那块内容读到 SDRAM 了,所以才非法指令呢。
可惜这个非 trusted 版本的 U-Boot, ST官方包中没有提供可以供 GDB 调试的 ELF 文件,所以没有调试信息参考,只有自己猜了。在这个状态下,不妨看看 SYSRAM 里面有什么内容代码。根据 PC 寄存器地址往前推一点:
> arm disassemble 0x2ffc2500 20
0x2ffc2500 0xea00000f B 0x2ffc2544
0x2ffc2504 0xe59ff014 LDR r15, [r15, #0x14]
0x2ffc2508 0xe59ff014 LDR r15, [r15, #0x14]
0x2ffc250c 0xe59ff014 LDR r15, [r15, #0x14]
0x2ffc2510 0xe59ff014 LDR r15, [r15, #0x14]
0x2ffc2514 0xe59ff014 LDR r15, [r15, #0x14]
0x2ffc2518 0xe59ff014 LDR r15, [r15, #0x14]
0x2ffc251c 0xe59ff014 LDR r15, [r15, #0x14]
0x2ffc2520 0x2ffc2540 SVC 0xfc2540
0x2ffc2524 0x2ffc2540 SVC 0xfc2540
0x2ffc2528 0x2ffc2540 SVC 0xfc2540
0x2ffc252c 0x2ffc2540 SVC 0xfc2540
0x2ffc2530 0x2ffc2540 SVC 0xfc2540
0x2ffc2534 0x2ffc2540 SVC 0xfc2540
0x2ffc2538 0x2ffc2540 SVC 0xfc2540
0x2ffc253c 0xdeadbeef CDPLE p14, 0x0a, c11, c13, c15, 0x07
0x2ffc2540 0xeafffffe B 0x2ffc2540
0x2ffc2544 0xea00002a B 0x2ffc25f4
0x2ffc2548 0xe10f0000 MRS r0, CPSR
0x2ffc254c 0xe200101f AND r1, r0, #0x1f
有一点像 Cortex-A7 的中断向量表了。我在镜像文件中找一下这段在哪里……
结果就在文件开头 0x100 偏移处
而且,内存中 0x2ffc2400 开始与文件对齐的话,很大一段内容也相同(前64kB都一样)。因此我怀疑 ROM bootloader 将 FSBL 读到 0x2FFC2400 这个地址,然后从某个地方开始执行的。
st wiki 上的这段,验证了我的猜测:
2.2 Image loading↑
The ROM code loads the image into SYSRAM internal memory at address 0x2ffc2400.
2.3 FSBL authentication↑
ROM code implements authentication process as described in STM32MP15 secure boot, Authentication processing chapter.
2.4 Jump to FSBL↑
If image authentication is successful, the ROM code stores the address of the boot context in r0 register and jumps to the FSBL entry point defined in image header.
The boot context (see boot_api_context_t structure in ) is in the first 512 bytes of SYSRAM. It contains information on boot process (such as the selected boot device) and pointers to the ROM code services (used for secure boot authentication).
--------------------------------------------------------------------------------------------------------
看来,ROM bootloader 将 SD 卡中的 FSBL 载入到 SYSRAM (总共256kB)之后,先进行校验,如果是合法的,就跳转到它指定的入口地址执行。这个入口地址,以及其它重要的描述数据,包含在 FSBL 的文件头里面:
查看 u-boot-spl.stm32-stm32mp157a-dk1-basic 文件内容,可知入口地址是 0x2ffc2500,和我猜的一样。那么 ROM 程序如何定位到 SD 卡中的 FSBL ? wiki上是这么解释的:
4.3 Boot from SD↑
SD cards contain two versions of FSBL. The ROM code tries to load and launch the first copy. In case of failure, it then try to load the second copy.
The ROM code first looks for a GPT. If it finds it, it locates two FSBLs by looking for the two first GPT entries which name begins with "fsbl". If it cannot find a GPT, the ROM code looks for FSBL1 at offset LBA34 and FSBL2 at offset LBA546.
---------------------------------------------------------------
于是,过程是这样的:STM32MP157上电以后,执行 ROM bootloader 程序。到满足 SD 卡启动条件时,会从 SD 卡的分区表(GPT格式,非传统PC用的MBR分区表)中找到前两个叫做 "fsbl" 的分区,读取其内容。若找不到GPT分区表,就从扇区地址 34 读取。如果这个 FSBL 校验没有通过,再尝试备份用的第二个 fsbl (没有GPT时位于扇区546)
FSBL (u-boot-spl.stm32-stm32mp157a-dk1-basic) 执行时,进行一些硬件初始化工作,然后搜索 SSBL,也就是 U-Boot (u-boot-stm32mp157a-dk1-basic.img),并装入到 SDRAM 中执行。U-Boot 的功能比较多,可以通过串口终端与用户交互。在 U-Boot 命令提示符下输入 help 可以列出所有命令:
U-Boot 2018.11-stm32mp-r4 (Nov 14 2018 - 16:10:06 +0000)
CPU: STM32MP157AAC Rev.B
Model: STMicroelectronics STM32MP157A-DK1 Discovery Board
Board: stm32mp1 in basic mode (st,stm32mp157a-dk1)
Board: MB1272 Var1 Rev.C-01
DRAM: 512 MiB
Clocks:
- MPU : 650 MHz
- MCU : 208.878 MHz
- AXI : 266.500 MHz
- PER : 24 MHz
- DDR : 533 MHz
****************************************************
* WARNING 500mA power supply detected *
* Current too low, use a 3A power supply! *
****************************************************
NAND: 0 MiB
MMC: STM32 SDMMC2: 0
Loading Environment from EXT4... OK
In: serial
Out: serial
Err: serial
Net: eth0: ethernet@5800a000
Hit any key to stop autoboot: 0
STM32MP> help
? - alias for 'help'
adc - ADC sub-system
base - print or set address offset
bdinfo - print Board Info structure
blkcache- block cache diagnostics and control
bmp - manipulate BMP image data
bootefi - Boots an EFI payload from memory
bootm - boot application image from memory
bootp - boot image via network using BOOTP/TFTP protocol
bootz - boot Linux zImage image from memory
chpart - change active partition
......
默认的配置(自动执行)是从 SD 卡获取配置然后载入 Linux 内核程序。通过终端打印的信息可以判断:
Boot over mmc0!
switch to partitions #0, OK
mmc0 is current device
Scanning mmc 0:4...
Found U-Boot script /boot.scr.uimg
1628 bytes read in 1 ms (1.6 MiB/s)
## Executing script at c4100000
Scanning mmc 0:4...
Found /mmc0_stm32mp157a-dk1_extlinux/extlinux.conf
Retrieving file: /mmc0_stm32mp157a-dk1_extlinux/extlinux.conf
235 bytes read in 1 ms (229.5 KiB/s)
Retrieving file: /splash.bmp
46180 bytes read in 5 ms (8.8 MiB/s)
1: stm32mp157a-dk1-sdcard
Enter choice: 1: stm32mp157a-dk1-sdcard
Retrieving file: /uImage
6722112 bytes read in 479 ms (13.4 MiB/s)
append: root=/dev/mmcblk0p6 rootwait rw console=ttySTM0,115200
Retrieving file: /stm32mp157a-dk1.dtb
71751 bytes read in 6 ms (11.4 MiB/s)
## Booting kernel from Legacy Image at c2000000 ...
Image Name: Linux-4.19.94
Created: 2020-01-09 9:19:10 UTC
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 6722048 Bytes = 6.4 MiB
Load Address: c2000040
Entry Point: c2000040
Verifying Checksum ... OK
## Flattened Device Tree blob at c4000000
Booting using the fdt blob at 0xc4000000
XIP Kernel Image ... OK
Using Device Tree in place at c4000000, end c4014846
Starting kernel ...
U-Boot 输出的信息里面提到了若干文件,它们应该是在某个分区当中——很可能就是 st-image-bootfs-openstlinux-weston-stm32mp1.ext4 这个镜像文件里面。到 Linux 环境下确认一下:
root@stm32mp1:/# mount | grep mmc
/dev/mmcblk0p6 on / type ext4 (rw,relatime)
/dev/mmcblk0p4 on /boot type ext4 (rw,relatime)
/dev/mmcblk0p7 on /usr/local type ext4 (rw,relatime)
/dev/mmcblk0p5 on /vendor type ext4 (rw,relatime)
root@stm32mp1:/# ls /boot -l
total 7230
-rwxr-xr-x 1 root root 1628 Mar 9 2018 boot.scr.uimg
drwx------ 2 root root 12288 Feb 18 2020 lost+found
drwxr-xr-x 2 root root 1024 Mar 9 2018 mmc0_stm32mp157a-dk1-optee_extlinux
drwxr-xr-x 2 root root 1024 Mar 9 2018 mmc0_stm32mp157a-dk1_extlinux
drwxr-xr-x 2 root root 1024 Mar 9 2018 mmc0_stm32mp157c-dk2-optee_extlinux
drwxr-xr-x 2 root root 1024 Mar 9 2018 mmc0_stm32mp157c-dk2_extlinux
drwxr-xr-x 2 root root 1024 Mar 9 2018 mmc0_stm32mp157c-ed1-optee_extlinux
drwxr-xr-x 2 root root 1024 Mar 9 2018 mmc0_stm32mp157c-ed1_extlinux
drwxr-xr-x 2 root root 1024 Mar 9 2018 mmc0_stm32mp157c-ev1-optee_extlinux
drwxr-xr-x 2 root root 1024 Mar 9 2018 mmc0_stm32mp157c-ev1_extlinux
drwxr-xr-x 2 root root 1024 Mar 9 2018 mmc1_stm32mp157c-ed1-optee_extlinux
drwxr-xr-x 2 root root 1024 Mar 9 2018 mmc1_stm32mp157c-ed1_extlinux
drwxr-xr-x 2 root root 1024 Mar 9 2018 mmc1_stm32mp157c-ev1-optee_extlinux
drwxr-xr-x 2 root root 1024 Mar 9 2018 mmc1_stm32mp157c-ev1_extlinux
drwxr-xr-x 2 root root 1024 Mar 9 2018 nand0_stm32mp157c-ev1_extlinux
drwxr-xr-x 2 root root 1024 Mar 9 2018 nor0-mmc1_stm32mp157c-ev1_extlinux
drwxr-xr-x 2 root root 1024 Mar 9 2018 nor0_stm32mp157c-ev1_extlinux
-rw-r--r-- 1 root root 46180 Mar 9 2018 splash.bmp
-rw-r--r-- 1 root root 71751 Mar 9 2018 stm32mp157a-dk1.dtb
-rw-r--r-- 1 root root 73626 Mar 9 2018 stm32mp157c-dk2-a7-examples.dtb
-rw-r--r-- 1 root root 74162 Mar 9 2018 stm32mp157c-dk2-m4-examples.dtb
-rw-r--r-- 1 root root 73578 Mar 9 2018 stm32mp157c-dk2.dtb
-rw-r--r-- 1 root root 68471 Mar 9 2018 stm32mp157c-ed1.dtb
-rw-r--r-- 1 root root 78340 Mar 9 2018 stm32mp157c-ev1-a7-examples.dtb
-rw-r--r-- 1 root root 78673 Mar 9 2018 stm32mp157c-ev1-m4-examples.dtb
-rw-r--r-- 1 root root 78033 Mar 9 2018 stm32mp157c-ev1.dtb
-rw-r--r-- 1 root root 6722112 Mar 9 2018 uImage
-rwxr-xr-x 1 root root 8192 Jan 1 1970 uboot.env
从已 mount 的文件系统可以看到,SD卡中写入的 .ext4 镜像文件对应于4个GPT分区,在 linux 下是4个mmc块设备。
/dev/mmcblk0p4 |
/boot |
st-image-bootfs-openstlinux-weston-stm32mp1.ext4 |
/dev/mmcblk0p5 |
/vendor |
st-image-vendorfs-openstlinux-weston-stm32mp1.ext4 |
/dev/mmcblk0p6 |
/ |
st-image-weston-openstlinux-weston-stm32mp1.ext4 |
/dev/mmcblk0p7 |
/usr/local |
st-image-userfs-openstlinux-weston-stm32mp1.ext4 |
具体到装载 Linux 内核的配置在这个配置文件里:
root@stm32mp1:/boot# cat mmc0_stm32mp157a-dk1_extlinux/extlinux.conf
# Generic Distro Configuration file generated by OpenEmbedded
MENU BACKGROUND /splash.bmp
TIMEOUT 20
LABEL stm32mp157a-dk1-sdcard
KERNEL /uImage
FDT /stm32mp157a-dk1.dtb
APPEND root=/dev/mmcblk0p6 rootwait rw console=ttySTM0,115200
uImage 就是 Linux 内核文件。于是,通过命令行参数指定 /dev/mmcblk0p6 块设备被 Linux 挂载为根文件系统,此后的事情就是 Linux 创建 init 进程,启动服务等等工作了。
|