1. 导学
1.1. 什么是系统移植
系统移植:把通用的操作系统放进非通用的计算机当中。
通用的操作系统:Linux
非通用的计算机:嵌入式设备
1.2. 为什么要学系统移植?
成品硬件不具备通用PC那样的通用性,多为定制化设计
1.3. 怎么移植系统?
1.3.1 难度等级1:有现成的系统镜像
现象:芯片厂商或者开发板厂商已经提供完整可启动镜像,直接烧录就能跑。
需要开发者对镜像、烧录有基本的认知、基本的实操能力即可
1.3.2 难度等级2:没有成品系统镜像,但提供可用的Boot程序镜像
现象:拿不到完整系统镜像,但厂商提供已经调通系统需的各种启动过程当中所需的软件的镜像,可以正常初始化基础硬件
需要开发者深度理解块设备的底层工作逻辑,具备组织各种镜像的能力。
1.3.3 难度等级3:有类似硬件的BSP可供参考
没有完全一模一样板子的BSP,但芯片厂商给出评估板、参考板完整源码包
需要开发者具备交叉编译复杂程序的能力,需要开发者对硬件有足够的认知,懂得如何与硬件工程协作解决遇到的软件问题的能力。
1.3.4 难度等级 4:只有主控芯片手册,没有任何可参考系统BSP
现象:只有芯片datasheet、参考手册,没有开源U‑Boot、内核 BSP。没有现成初始化代码。
挑战最大,真正从零BSP移植。
2. 块设备、dd命令与拨码开关
2.1. Linux系统烧录实验
2.实验目的:
- 直观了解嵌入式
Linux完整烧录流程,建立镜像烧录、硬件启动的基础认知,理解烧录的核心概念与实际用途。 - 掌握
Linux块设备识别方法,会使用lsblk、文件节点查询等命令,能区分存储块设备与其他硬件设备。 - 理解
dd命令原理与关键参数,熟练使用dd将系统镜像烧录至TF卡。 - 了解
STM32MP1开发板拨码开关配置逻辑,掌握SD卡启动硬件设置,实现开发板Linux正常启动。
2.实验原理:
Linux下U盘、TF卡等存储设备抽象为/dev下的块设备节点,通过节点完成硬件读写。dd为Linux底层读写工具,实现文件与块设备原始拷贝;配置if、of、bs、同步缓存等参数,可将镜像逐块烧录到TF卡;nocreat、fsync等参数防止写入异常、缓存丢失,保证烧录可靠。STM32MP1依靠拨码开关设置BOOT引脚电平选择启动介质;拨码设为101,开发板从TF卡加载镜像,完成初始化并启动Linux。
什么是烧录? 就是把编译好的系统镜像写入存储硬件,烧录是嵌入式设备部署系统的前置步骤。
2.实验步骤:
第一步 识别TF卡到Ubuntu系统
第二步 使用dd命令烧录镜像文件
第三步 启动STM32MP1使其进入到Linux系统
2.实验说明:
实验当中以$开头的代码代表其后是在shell下运行的命令
2.2. 识别TF卡到Ubuntu系统
2.2.1 识别设备到Ubuntu
通过读卡器或者带插卡槽的扩展坞,将TF卡连到PC机上。如果你使用的Ubuntu是VMware的虚拟机的话,会看到如图2-1-1的对话框。"Super Top Mass Storage Device"的意思是"大容量存储设备"。

2-1-1 VMware 的可移动设备管理器对话框
如果没有看到以上的对话,你也可以通过【虚拟机】-【可移动设备】然后找到相应的设备。在设备的子菜单里选择【连接(断开与主机的连接)】。如图2-1-2所示

2-1-2通过VMware 的主菜单连接存储设备
存储设备的各类很多,不一定识别到的设备都叫"Super Top Mass Storage Device"。例如图2-1-3所使用扩展坞当中的卡槽连接的Ubuntu系统。但是其名字是"Genesys Logic USB3.0 Card Reader"。这是个叫"创惟逻辑"的USB 3.0读卡器

2-1-3 创惟逻辑USB 3.0读卡器
也可以在右下角找到它,鼠标放到上面会有提示。如果设备连接成功以后,图标的右下角会被点亮,如2-1-4所示。

2-1-4 虚拟机的存储设备状态图标
用鼠标右击图标也可以看到连接设备的菜单,如图2-1-5所示。

2-1-5 存储设备状态图标的右击菜单
2.2.2 很像存储设备的STLink
连上STM32MP157以后,计算机会识别出一个叫STLink的设备。这个设备并不是一个真正意义上的存储设备,而是一个集调试、烧录、串口输出的一个开发工具。连接设备时,它也很容易被当成一个存储设备,这一点要格外注意。2-2-1就是VMware识别到的STLink,2-2-2是STLink实物。

2-2-1 VMware识别到的STLink

2-2-2 STLink
2.2.3 找到对应的存储设备
识别成功以后,可以通过ls命令在/dev目录下找到相应的设备文件。
$ ls -l /dev/sd*
brw-rw---- 1 root disk 8, 0 6月 14 13:35 /dev/sda
brw-rw---- 1 root disk 8, 1 6月 14 13:35 /dev/sda1
brw-rw---- 1 root disk 8, 2 6月 14 13:35 /dev/sda2
brw-rw---- 1 root disk 8, 3 6月 14 13:35 /dev/sda3
brw-rw---- 1 root disk 8, 16 6月 25 08:22 /dev/sdb
这里的每一行都以
b开头,代表它们是块设备文件。如果是d,就代表他们是目录。如果是-则代表它们是普通文件
也可以使用专门查看块设备的lsblk命令
$ lsblk
...
sdb 8:16 1 29.7G 0 disk
├─sdb1 8:17 1 236.5K 0 part
├─sdb2 8:18 1 236.5K 0 part
├─sdb3 8:19 1 2M 0 part
└─sdb4 8:20 1 29.7G 0 part
...
Linux系统会把类似磁盘的块设备以sd开头+a到z小写字母的方式命名,如sda、sdb等。一般最后一个块设备就是TF卡。上面这个是sdb,而下面这种情况就是sdc了。
sda 8:0 0 100G 0 disk
├─sda1 8:1 0 1M 0 part
├─sda2 8:2 0 513M 0 part /boot/efi
└─sda3 8:3 0 99.5G 0 part /var/snap/firefox/common/host-hunspell
/
sdb 8:16 1 0B 0 disk
sdc 8:32 1 29.7G 0 disk
├─sdc1 8:33 1 256K 0 part
├─sdc2 8:34 1 256K 0 part
├─sdc3 8:35 1 2M 0 part
├─sdc4 8:36 1 64M 0 part
└─sdc5 8:37 1 1.4G 0 part /media/linux/rootfs
不难发现,这里的sdb的大小是0B,极有可能是Ubuntu系统是把读卡器识别成sdb了。后续烧录自然不可能把数据烧录到这个文件当中的。
2.3. 使用dd命令烧录镜像文件
2.3.1. 准备镜像文件
在使用命令之前,需要首先找到课程资料里【固件镜像与根文件系统】的sdcard.img文件。在进行烧录之前需要把这个文件下载到Ubuntu系统里。如果你使用的Ubuntu是VMware的虚拟机,可以先把文件下载到宿主机。然后,通过VMware提供的共享文件夹把文件从宿主机复制到Ubuntu系统。也可以使用鼠标拖拽等方式直接把文件拽到虚拟机。

3-1-1使用共享文件夹传递文件

3-1-2 通过鼠标拖拽的方式完成文件的复制
镜像文件准备好以后,在你的工作目录下就可以通过ls命令看到sdcard.img文件了。
$ ls
sdcard.img
2.3.2. 确定tf卡对应的设备
使用前面讲到的lsblk命令可以确定哪个是TF卡。
$ lsblk
...
sdb 8:16 1 29.7G 0 disk
├─sdb1 8:17 1 236.5K 0 part
├─sdb2 8:18 1 236.5K 0 part
├─sdb3 8:19 1 2M 0 part
└─sdb4 8:20 1 29.7G 0 part
...
2.3.3. 执行dd命令烧录镜像
使用dd命令可以实现镜像的烧录
sudo dd if=sdcard.img of=/dev/sdb bs=4M conv=nocreat,fsync status=progress
输入root用户密码之后,等待1分钟左右时间即可完成烧录
$ sudo dd if=sdcard.img of=/dev/sdb bs=4M conv=nocreat,fsync status=progress
[sudo] linux 的密码:
记录了30+1 的读入
记录了30+1 的写出
128385024字节(128 MB,122 MiB)已复制,11.8394 s,10.8 MB/s
2.3.4. dd命令详解
dd命令是嵌入式Linux当中极常用的命令。
由于/dev/sdb是设备文件所以执行命令必须使用sudo以管理员 (root) 权限执行。
if和of,即input file和output file,一个输入文件,另一个为输出文件。本命令当中,输入文件为sdcard.img,输出文件为/dev/sdb。
/dev/sdb属于块设备,块设备是以块(Block)进行读写的,传统上认为块(Block)的大小是512个字节。dd命令在烧录数据时,也是以512B/次将数据写入到/dev/sdb的。512B/次的数据写入方式早已不再适用于现在的磁盘设备了。所以将512B调整为4M,以最大化增加数据的读写速度。
dd默认情况下,静默无任何输出的,烧写数据又需要一定的时间,如果静默无任何输出的话,会让人产生系统卡死的错觉。如果想要看到实时烧写进度,就需要加上status=progress的设定。
如果在/dev目录下不存在sdb文件,执行dd命令,也不会提示烧写失败。这是因为dd命令会按照of的指示创建一个同名的普通文件。
$ ls /dev/sd* -l
brw-rw---- 1 root disk 8, 0 9月 1 08:27 /dev/sda
brw-rw---- 1 root disk 8, 1 9月 1 08:27 /dev/sda1
brw-rw---- 1 root disk 8, 2 9月 1 08:27 /dev/sda2
brw-rw---- 1 root disk 8, 3 9月 1 08:27 /dev/sda3
-rw-r--r-- 1 root root 2134900736 9月 1 09:21 /dev/sdb
也就是说,看起来dd命令执行成功了。但实际上,数据全部写进了与sdb同名的普通文件里了。
为了防止这种情况,增加conv=nocreat的设定。
fsync的作用是强制把缓冲区数据刷入物理设备,写完才返回。 避免命令结束但数据还在内存缓存当中。
2.4. 启动STM32MP1使其进入到Linux系统
2.4.1 什么是拨码开关?
拨码开关(DIP Switch,双列直插开关),是一种手动拨动的微型机械式开关,多焊接在 PCB 电路板上,靠拨动拨片来切换通 / 断状态,用来设置硬件二进制配置参数。

4-1-1 拨码开关
2.4.2 STM32MP1开发板上的拨码开关在

4-2-1 STM32MP157开发板上的拨码开关
2.4.3 拨码开关是如何工作的?
2.4.3.1 开关上的0和1

4-3-1 拨码开关的0和1
2.4.3.2 拨码开关和BOOT引脚
根据顶层丝印图关于拨码开头摆放方式和电路原理图的对应关系,可以很轻易的确定出,拨码开关与BOOT引脚的对应关系。

4-3-2 拨码开关与Boot引脚
2.4.3.3 原理总结
根据以上分析,手动改变拨码开关的0和1,会直接影响SoC的BOOT引脚的0和1。SoC会根据BOOT引脚的值选择不同的启动方式。BOOT引脚和启动方式对应关系如图所示:

4-3-3a 电路原理图当中关于BOOT引脚与启动方式说明
其中,010为eMMC启动、101为SD-Card启动,后面主要使用这两种启动方式。
eMMC是电路板上用于数据存储的芯片,其功能定位类似于PC机上的硬盘。图4-3-3b 是STM32MP157开发板上的eMMC的位置。
SD-Card指的是TF卡的插卡槽。

4-3-3b STM32MP157开发板上的eMMC
2.4.3.4 通过SD-Card启动Linux系统
启动方式的意思就是,SoC从哪个设备上启动Linux系统。所以要想在TF卡上启动Linux系统,需要将拨码开关设置为101。
首先,使用串口工具连接好串口。然后,手动将拨码开关设置为101。插上电源,即可看到打印信息。如果Linux系统可以正常启动,你将看到类似fsmp1a login:的命令提示。此时,输入root即可进入到Linux系统。

4-3-4 通过TF卡启动Linux系统
3. 镜像文件与块设备
3.1. Linux系统镜像文件的组成
3.1.1 什么是镜像文件?
镜像文件是对整个块设备(磁盘/分区)或文件系统的完整数据副本。分区指将存储设备的存储空间划分成逻辑上独立的几个部分
3.1.2 sdcard.img镜像文件里的四个分区
通过sgdisk命令可以看到sdcard.img镜像文件的结构信息:
$ sudo sgdisk -p sdcard.img
Disk sdcard.img: 250752 sectors, 122.4 MiB
Sector size (logical): 512 bytes
Disk identifier (GUID): 2B25ADD6-5D12-49DD-B000-16B3D49D018F
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 250718
Partitions will be aligned on 2-sector boundaries
Total free space is 5 sectors (2.5 KiB)
Number Start (sector) End (sector) Size Code Name
1 34 445 206.0 KiB 8300 fsbl1
2 446 857 206.0 KiB 8300 fsbl2
3 858 4953 2.0 MiB 8300 ssbl
4 4954 250713 120.0 MiB 8300 rootfs
可以看到镜像文件有fsbl1、fsbl2、ssbl、rootfs四个分区。
┌───────┬───────┬──────┬────────────────────────────────────┐
│ fsbl1 │ fsbl2 │ ssbl │ rootfs │
└───────┴───────┴──────┴────────────────────────────────────┘
(sdcard.img的分区信息)
3.1.3 文件系统
其中,rootfs分区存在一个文件系统 。所谓文件系统,就是块设备上数据存储的一套规则。本质上就是按照某种规则存储在块设备上的数据。有了这个数据以后就可以通过cd、ls、pwd等常见的普通文件相关的shell命令像访问磁盘文件一样访问它们了。磁盘文件可以被访问,本质上也是因为磁盘之上按照某种规则存储了文件系统相关的数据。
例如,将前文烧录好的TF卡连接到Ubuntu之后,可以看到如下信息:
$ lsblk
....
sdb 8:32 1 29.7G 0 disk
├─sdb1 8:33 1 236.5K 0 part
├─sdb2 8:34 1 236.5K 0 part
├─sdb3 8:35 1 2M 0 part
└─sdb4 8:36 1 29.7G 0 part /media/linux/rootfs
....
很多时候Ubuntu会自动将块设备上的文件系统安装到Ubuntu系统当中的某个目录里,这个过程称为挂载 ,这个目录称为挂载点 。挂载之后就可以通过相应的目录访问文件系统了。这里的/media/linux/rootfs就是sdb4上的文件系统的挂载点。
$ ls /media/linux/rootfs/
bin home lib32 lost+found mnt proc run srv tmp var boot
dev etc lib linuxrc media opt root sbin sys usr
备注 :课程资料提供的rootfs里的文件会不定期进行更新,所以你看到的文件系统和这里的不完全一致,也是正常的
3.1.4 tf-a和u-boot
另外三个分区里保存的是Linux启动所需的其它两个文件,这两个文件分别TF-A和U-boot。TF-A和U-boot并不是具体的文件名,而是两种软件。这两种软件在开发板启动初期负责初始化开发板。之后加载并启动Linux系统。
bash
TF-A U-boot
│ │
┌───┼───┬───────┬───┼──┬────────────────────────────────────┐
│ fsbl1 │ fsbl2 │ ssbl │ rootfs │
└───────┴───┼───┴──────┴────────────────────────────────────┘
│
TF-A
3.2. 存储设备基本概念
3.2.1 扇区
扇区(Sector)是存储设备(磁盘/U盘/SSD硬盘等)中最小的存储单元。

2-1-1 硬盘的构造
如图所示,中间层层堆叠的银白色盘片就是磁盘 。每个磁盘上有无数条磁道(Track) 。硬盘上所有盘片上半径相同的磁道,组成一个柱面(Cylinder) 。从盘片的圆心出发,向外画直线,可以将磁道划分为若干弧段,每条磁道上的每个弧段就称为一个扇区,每个扇区存储512字节的数据。

2-1-2 磁道与扇区
对磁盘来说,扇区是其最小物理存储单元,而对于U盘/SSD硬盘而言,其存储实现和磁盘完全不同,但它们也有扇区,该扇区是抽象后的概念。总之,这里最重要也是最基本的概念是:扇区表示存储设备的最小存储单元。
传统硬盘各磁道扇区数一致,外圈磁道扇区弧段更长,磁颗粒密度更低,造成空间浪费。等密度磁道技术出现后,各磁道磁颗粒密度相同、单扇区容量不变,需按弧长划分扇区:外圈周长更大,扇区数量更多,不同磁道扇区数不再相等。
原先硬盘依靠每磁道扇区数相同,使用CHS(柱面、磁头、扇区)寻址,操作系统可把线性地址换算为CHS直接定位物理扇区。磁道扇区数不等后,该换算公式不再适用。
于是引入LBA逻辑块 寻址:上层直接下发逻辑扇区地址,由硬盘控制器完成逻辑到物理地址的转换,简化上层工作,但上层无法直接获知物理位置。同时寻址上限大幅提升:CHS为24位,最大寻址约8GB;LBA为48位,可达128PB,提升约1600万倍。
现代硬盘扇区实际容量也进行了升级。从原来的512字节升级为4KB(4096字节)。传统512字节扇区附带校验等额外开销,硬盘容量越大,开销占比越高;改用4KB物理扇区能够减少开销,提升空间利用率。
但Linux系统等上层软件仍沿用512字节扇区的逻辑。为实现兼容,硬盘对外接口模拟512字节扇区:上层读写512字节时,硬盘内部实际读取完整4KB物理扇区,再返回对应512字节数据。即上层看到的是逻辑512B扇区,硬盘真实物理扇区为4KB。
知识点总结:扇区是存储设备的最小的存储单元;每个扇区的大小为512B(尽管底层硬件的实现已经不是这样)。
3.2.2 分区和分区表
分区指将存储设备的存储空间划分成逻辑上独立的几个部分,每个部分就是一个独立的分区。例如Windows下的C盘、D盘等,就是一个个的分区,Linux下的sda1、sda2等,也是一个个的分区。
分区的种类和一些实现细节,由于篇幅原因,加之课程只是对存储设备进行简单分区,所以这里不再讨论关于分区 的具体细节,只对进行分区时可能会遇到的概念进行讲解。
需要了解的分区是MBR和GPT分区两类;
3.2.2.1. MBR分区
MBR分区是传统的分区方式。MBR分区的分区的数量受限,而且单个分区的大小也不能超过2TB。所以需要另外一种分区------GPT。
GPT(GUID Partition Table)分区支持2TB以上的分区,分区的数量也不受限制(但分区数量受操作系统的限制)。
不管采用哪种分区形式,都需要在在硬盘上保存分区信息,这个分区信息就是分区表。不同的分区形式的分区表在硬盘上的位置和数据结构都会有所不同。在进行分区时不必考虑这些细节,因此这里不再赘述。
3.3.复刻一个新的Linux系统镜像
实验目的 : 建立对系统镜像文件建立感性认知,理解SD卡镜像内部GPT分区布局,掌握loop虚拟块设备用法,理解镜像从0到1的生成流程,区分原始镜像文件与真实物理TF卡块设备的异同。
实验原理 : 使用前面学到的dd命令生成全0空白镜像文件,配合sgdisk命令完成GPT分区表初始化,借助Linux loop设备将普通文件模拟成块设备,分别写入TF‑A、U‑Boot启动固件,为rootfs分区创建ext4文件系统并填充根文件系统内容,最终得到一份可以直接烧录到TF卡运行的完整sdcard镜像文件
实验步骤:
dd命令生成镜像文件- 使用
parted命令和sgdisk命令完成分区信息的初始化 - 通过
loop设备烧录TF-A和U-boot - 创建文件系统并写入系统文件
实验说明 :实验当中以$开头的代码代表其后是在shell下运行的命令
3.3.1 dd命令生成空白镜像文件
我们需要先生成一个全部填充0字节的空白镜像文件,用来模拟一张还没有任何数据的TF卡。参考原sdcard.img大小,这里生成122MiB空白镜像。
$ sudo dd if=/dev/zero of=sdcard.img count=250752
if=/dev/zero:输入源,zero设备会源源不断输出0字节;of=sdcard.img:输出为我们新建的镜像文件;bs=512:扇区大小512字节;count=250752:总扇区数量,和课程资料里的sdcard.img大小类似。
执行完成后,可以使用ls命令查看生成的文件:
$ ls -lh sdcard.img
注意:此时
sdcard.img仅仅是一个全0普通文件,没有分区表,没有任何分区,不能直接烧录运行。
3.3.2 为镜像文件创建并初始化分区
sudo sgdisk --resize-table=128 -a 1 -n 1:34:445 -c 1:fsbl1 -n 2:446:857 -c 2:fsbl2 -n 3:858:4953 -c 3:ssbl -n 4:4954:250713 -c 4:rootfs -A 4:set:2 -p sdcard.img -g
拓展说明:
parted也可以完成GPT分区操作,sgdisk对GPT支持更加友好,本实验优先使用sgdisk。此时镜像文件已经具备GPT分区表,但分区内全部还是0,没有固件、没有文件系统。
3.3.3 通过loop设备烧录TF-A和U‑Boot固件
loop设备可以把普通.img文件虚拟成一个块设备,就如同真实TF卡/dev/sdb,系统会自动生成对应的 loop分区设备/dev/loopXp1、/dev/loopXp2、/dev/loopXp3、/dev/loopXp4,分别对应 fsbl1、fsbl2、ssbl、rootfs四个分区。
3.3.3.1 将镜像文件关联到loop设备
$ sudo losetup --partscan --show --find sdcard.img
命令输出示例:/dev/loop0,代表当前镜像绑定到 loop0 设备。
--partscan:让内核扫描镜像内部 GPT 分区,自动生成 loop0p1~loop0p4 分区设备节点。
通过ls命令查看生成的loop分区节点:
$ ls /dev/loop0*
/dev/loop0 /dev/loop0p1 /dev/loop0p2 /dev/loop0p3 /dev/loop0p4
注意:你的机器不一定是
loop0,以命令输出为准,下文全部替换为你实际得到loop设备编号。
3.3.3.2 将TF‑A固件写入fsbl1、fsbl2分区
fsbl1与fsbl2存放完全相同的TF‑A(ATF)固件,双分区设计用于固件容错升级。
课程资料中【固件镜像与根文件系统】准备好tf‑a.stm32文件。使用dd写入loop0p1、loop0p2:
$ sudo dd if=tf-a-stm32mp157a-fsmp1a.stm32 of=/dev/loop0p1 bs=4M conv=nocreat,fsync
$ sudo dd if=tf-a-stm32mp157a-fsmp1a.stm32 of=/dev/loop0p2 bs=4M conv=nocreat,fsync
3.3.3.3 将U‑Boot固件写入ssbl分区
ssbl分区存放U‑Boot固件,对应资料文件u-boot.stm32:
$ sudo dd if=u-boot.stm32 of=/dev/loop0p3 bs=4M conv=nocreat,fsync
此时fsbl1、fsbl2、ssbl三个裸数据分区已经写入完成。rootfs分区目前仍然只是一块空白块设备,还没有文件系统。
3.3.4 创建文件系统并写入系统文件
rootfs分区对应的loop设备节点为/dev/loop0p4,需要在此块设备上构建ext4文件系统,之后把根文件系统全部文件解压进去。
3.3.4.1 创建 ext4 文件系统
$ sudo mkfs.ext4 /dev/loop0p4
该命令等价于Windows下格式化磁盘,会在分区内部写入ext4文件系统元数据。
3.3.4.2 挂载 rootfs 分区,填充根文件系统
创建临时挂载目录,将loop0p4挂载到该目录:
$ mkdir -p /mnt/rootfs
$ sudo mount /dev/loop0p4 /mnt/rootfs
挂载完成后,/mnt/rootfs就对应rootfs文件系统根目录。我们可以把课程资料【固件镜像与根文件系统】提供的rootfs.tar解压到该目录。
$ sudo tar -xf rootfs.tar -C /mnt/rootfs
注意:这里的-C的作用是解压之前,先切换到目标目录,再把文件释放到这个目录下
3.3.4.3 解压完成,可以查看目录下文件
$ ls /mnt/rootfs
bin boot dev etc home lib lib32 lost+found media mnt opt proc root run sbin srv sys tmp usr var
3.3.4.4 卸载分区,释放loop设备
文件全部写入完成,必须执行sync强制刷缓存,再卸载,最后释放loop设备,否则镜像文件数据可能会出现不完整的情况。
$ sudo sync
$ sudo umount /mnt/rootfs
$ sudo losetup -d /dev/loop0
losetup -d:解除镜像文件与 loop 设备的绑定。执行完该命令,
/dev/loop0p1‑p4节点消失。
到此,sdcard.img就是我们手动复刻完成的完整镜像文件。
3.3.4.5 验证复刻出来的镜像,查看镜像分区信息
$ sudo sgdisk -p sdcard.img
检查 sdcard.img,分区扇区、分区是否正确。
3.3.5 使用dd烧录sdcard.img到TF卡测试
按照实验 1 的步骤,识别 TF 卡块设备,执行 dd 烧录:
$ sudo dd if=sdcard.img of=/dev/sdb bs=4M conv=nocreat,fsync status=progress
/dev/sdb 替换成你的 TF 卡设备节点,千万不要写错设备,避免破坏本地硬盘。
烧录完成,开发板拨码开关设置为101(SD‑Card启动),上电,串口观察打印。如果复刻成功,可以正常输出U‑Boot日志,最终输出fsmp1a login:登录提示符,输入root登录系统。
4.第四章 嵌入式Linux系统的启动流程
4.1. 前置知识
4.1.1 ROM和RAM
- ROM(只读存储器) :掉电数据不丢失;数据写入后一般不可修改。 分类:
- ROM:出厂固化,不可改写,成本低,用于开机启动。
- PROM:用户只能编程写入1次。
- EPROM:可多次擦写改写。
- EEPROM:原理同EPROM,擦除操作更简便。
- RAM(随机存取存储器) :读写速度和存储位置无关,掉电数据丢失 。
- SRAM(静态RAM):速度快、价格高,用作高速缓存Cache。
- DRAM(动态RAM):用作计算机主内存。
- Flash Memory(闪存) :高密度非易失存储器,兼具
EEPROM与RAM特性,主流分为Nor Flash、Nand Flash:- Nor Flash:支持字节寻址,可芯片内直接运行程序(
XIP);读快、写慢。嵌入式常用存启动代码。 - Nand Flash:串行块读写,不能
XIP;存储密度高、成本低。用于存放普通数据。
- Nor Flash:支持字节寻址,可芯片内直接运行程序(
历史:
1988年Intel推出Nor Flash;1989年东芝推出Nand Flash。 嵌入式常见搭配:小容量Nor Flash放启动代码,Nand Flash存放其余数据。
4.1.2. bootloader
集成电路只读存储器问世前,早期ENIAC计算机没有存储程序,依靠接线配置完成运算,不存在自举电路、引导程序,也没有BootLoader与Boot概念。
1952‑1956年的IBM 701设有Load按键,按下后可从穿孔卡片、磁带、磁鼓(由选择开关选定)向主存载入36位初始字;后续18位半字作为指令,继续加载更多数据,由此运行引导程序。这和现代嵌入式用拨码开关选择SD卡、Flash启动的思路很相似。1958年,Boot正式成为计算机术语。
注意:
Load按键属于人工外部操作,而Loader是软件实现、自动执行的加载器本质不同。
Boot源自Bootstrap(鞋带),出自谚语 "Pull oneself up by one's own bootstraps",本意 "拽鞋带把自己提起来",比喻不可能完成的事。早期已有自举振荡器这类叫法。计算机启动面临悖论:要运行程序才能开机,不开机又跑不了程序。人们先往内存置入一小段代码,让机器能够继续运行,该过程就被比喻为 "拉鞋带",简称 Boot。
随着处理器演进,Boot负责的工作不断增加:配置CPU运行模式、内部时钟,以及Cache、MMU等。
4.2. 通用的启动流程
4.2.1 通用的启动流程
ST官方认为,通用的流程可以分为五步,如图2-1-1所示。

2-1-1 ST官方认为的Linux系统启动的通用流程
4.2.2 ROM code
第一步:ROM code完成基本时钟树的初始化,然后从启动设备加载FSBL(First Stage Boot Loader),也就是第一级启动加载程序。之后启动FSBL。
这里的FSBL,指的并不是某个具体的程序,而是一类程序。关于这一点会在七章详细讲解。而ROM,则是SoC内部的存储器。例如STM32MP157A就内置了一块ROM,这一点可以通过【设备相关资料\FSMP1A\datasheet\00-STM32MP157】《STM32MP157a-规格书》的第19页的【STMMP157a的内部结构】框图得到证实。

2-2-1 STM32MP157A里的ROM
在课程资料【设备相关资料\FSMP1A\datasheet\00-STM32MP157】的《stm32mp157-数据手册》的第157页的内存映射图同样也可以找到关于ROM的映射信息。

2-2-2 数据手册的内存映射图
从以上资料,不难发现STM32MP157a配备的ROM的大小是128KB。这块ROM是专为从Cortex-A7启动的程序提供服务的。这里的BOOT是暗示这块内存里保存的是CPU核的启动代码。ROM里保存的是Cortex-A7的启动代码,RETRAM则是Cortex-M4核的启动代码。
ROM里保存的启动代码也被称为ROM Code 或者Boot Code 。
关于STM32MP系列SoC芯片的ROM Code,ST官方在STM32_MPU_ROM_code_overview提供了关于ROM code的使用说明。
这里有个关键问题,ROM Code是怎么确定该从哪个启动设备加载FSBL的?
通过第二章的Linux系统烧录实验可以很容易的得到答案。ROM code会通过boot引脚确定需要从哪个启动设备上获取系统启动代码。这里的启动设备就是之前所提到的TF卡、eMMC等存储设备,也就是Linux系统里的块设备。确定启动设备以后ROM code从启动设备将FSBL加载到SoC内置的RAM里。之后启动FSBL。
4.2.3 关于内置RAM
第二步:FSBL会完成时钟树的进一步初始化,并且还会初始化外部RAM控制器,最后从启动设备(存储器或者串口)加载SSBL。同FSBL一样,SSBL指的也不是具体的某个程序。
注意,此时FSBL是运行在内置的RAM的。由于空间有限,所以内置的RAM的大小一般都比较小。例如STM32MP157内置的RAM就只有256KB。如图2-3-1 所示,就是数据手册里的SYSRAM与ROM。之所以需要初始化外部的RAM,是由于SSBL的大小一般会超过256KB,他们会被加载到外置的RAM当中。

2-3-1 数据手册里的ROM和SYSRAM
4.2.4 外置的RAM与后续的启动流程
STM32MP157开发板上的外置RAM是一块大小为4Gb,即512MB的DDR RAM。图2-4-1是STM32MP157开发板的设计者提供的DDR的规格书的首页,规格书当中标明了DDR的大小为4Gb,这里是小写b,代表bit,大写B代表byte,字节。所以STM32MP157开发板上搭载的是一块大小为512MB的DDR RAM。

2-4-1 DDR的规格书首页截图
总体上,我们可以将以上的启动流程总结为从开发板上电开始,CPU会自动执行ROM code,ROM code会将FSBL加载到内置的RAM,这个内置的RAM在STM32MP157A上就是SYSRAM。FSBL启动之后再将SSBL加载到外置的RAM当中,也就是STM32MP157开发板的DDR ROM,并启动SSBL。
如果一切顺利,以上流程都会自动完成,无需人工干预。SSBL启动以后,一个最小系统就运行起来了。此时,SSBL会提供一个简单的Shell环境,供用户进行硬件测试或启动Linux系统。
所以,Linux系统的启动方式也是可以通过SSBL提供的Shell环境进行个性化配置的。在第二章的《Linux系统烧录实验》之所以会自动进入到Linux系统,是由于课程提供的sdcard.img已经配置好了启动Linux系统的方式。如何配置Linux系统的启动方式,以及通用步骤的最后三步,将在第五章当中进行讲解。
4.3. STM32MP157的启动流程
4.3.1 STM32MP157非安全启动流程
STM32MP系列产品提供两种启动流程:
安全启动流程和非安全启动流程。
非安全启动流程已经被新版的软件代码弃用,所以不再赘述,重点介绍安全启动。

3-1-1 STM31MP的非安全启动流程
4.3.2 STM32MP157安全启动流程
如图3-2-1 所示,STM32MP157的安全启动流程当中,TF-A就是FSBL,U-boo是SSBL。TF-A程序除了加载启动U-boot之外,还有可能会启动OP-TEE,之后OP-TEE之上会运行一些可信的应用程序。当然OP-TEE是可选的启动项。

3-2-1 STM31MP的安全启动流程
另外,由于STM32MP157是异构的的芯片架构,所以,除了标准的Linux启动 流程之外,还可以启动使用Cortex-M4工作的RTOS系统。
什么是TF-A?
TF-A( Trusted Firmware‑A),可信固件,是根植于ARM体系结构的开源项目,这里的A代表的是Cortex-A系列的CPU架构。相应地Cortex-M也有TF-M实现。
什么是u-boot?
U‑Boot(Universal Bootloader,通用引导加载程序),是嵌入式领域最主流的开源二级引导程序(SSBL)。
值得注意的是,TF-A和OP-TEE运行在SYSRAM当中,而U-boot、Linux运行在外置的DDR RAM当中。
5.第五章 U-boot是如何启动Linux系统的?
5.1. U-boot的常用命令
5.1.1 自启动模式与交互模式
5.1.1.1 自启动模式
uboot启动后若没有用户介入,倒计时结束后会启动u-boot环境变量bootcmd中设置的命令。
....
Hit any key to stop autoboot: 0
Boot over mmc0!
switch to partitions #0, OK
mmc0 is current device
Scanning mmc 0:4...
Found /boot/extlinux/extlinux.conf
....
5.1.1.2 交互模式
倒计时结束之前按下任意按键,uboot会进入交互模式,交互模式下用户可输入U-boot内置的Shell命令以完成相应的操作。
Hit any key to stop autoboot: 0
FSMP1A>
FSMP1A>
这里的FSMP1A>是U-boot的命令提示符。
5.1.2 帮助命令
输入help会看到所有当前U-boot支持的所有命令及其说明。
FSMP1A> help
? - alias for 'help'
adtimg - manipulate dtb/dtbo Android image
base - print or set address offset
bdinfo - print Board Info structure
blkcache - block cache diagnostics and control
bmp - manipulate BMP image data
boot - boot default, i.e., run 'bootcmd'
bootcount - bootcount
也可以输入help+相应的命令,查看该命令的用法。如help bootm:
FSMP1A> help bootm
bootm - boot application image from memory
Usage:
bootm [addr [arg ...]]
- boot application image stored in memory
passing arguments 'arg ...'; when booting a Linux kernel,
'arg' can be the address of an initrd image
When booting a Linux kernel which requires a flat device-tree
a third argument is required which is the address of the
device-tree blob. To boot that kernel without an initrd image,
use a '-' for the second argument. If you do not pass a third
a bd_info struct will be passed instead
For the new multi component uImage format (FIT) addresses
must be extended to include component or configuration unit name:
addr:<subimg_uname> - direct component image specification
addr#<conf_uname> - configuration specification
Use iminfo command to get the list of existing component
images and configurations.
Sub-commands to do part of the bootm sequence. The sub-commands must be
issued in the order below (it's ok to not issue all sub-commands):
start [addr [arg ...]]
loados - load OS image
ramdisk - relocate initrd, set env initrd_start/initrd_end
fdt - relocate flat device tree
cmdline - OS specific command line processing/setup
bdt - OS specific bd_info processing
prep - OS specific prep before relocation or go
go - start OS
5.1.3 环境变量相关命令
5.1.3.1 查看环境变量
查看环境变量可以使用printenv命令。
FSMP1A> help printenv
printenv - print environment variables
Usage:
printenv [-a]
- print [all] values of all environment variables
printenv -e [-guid guid][-n] [name ...]
- print UEFI variable 'name' or all the variables
"-guid": GUID xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
"-n": suppress dumping variable's value
printenv name ...
- print value of environment variable 'name'
printenv -a是查看所有的环境变量,也可以简写成pri,效果是一样的。printenv加上环境变量的name就可以查看环境变量的值。如bootcmd:
printenv bootcmd
bootcmd=run bootcmd_stm32mp
5.1.3.2 设置环境变量
设置环境变量可以使用setenv,如
setenv bootdelay 3
5.1.3.3 保存环境变量
如果设置的环境变量不保存的话,对环境变量进行的所有操作都会在重启以后立即失效。如:
saveenv
此时,u-boot重启以后就会倒计时3秒才会进入自启动模式了。可以手动按下复位键或使用reset命令重启U-boot。可以看到Hit any key to stop autoboot: 后面数字已经变成从3开始倒计时了。
5.1.3.4 删除环境变量
删除环境变量很简单,只需要setenv+环境变量的名字,之后不加任何内容,代表删除这个环境变量。如:
setenv ipaddr
5.1.3.5 重置环境变量
如果环境变量设置错误了,想要恢复最初设置,可以使用env default -a。
env default -a
当然设置完了以后记得要保存,不然重启后会立即失效。
saveenv
5.2. Linux系统是如何被加载启动的?
了解完U-boot的基本使用方法之后,就可以讨论第四章的第二小节所提到的Linux系统的通用启动流程的第三步了。

2-1-1 Linux系统通用启动流程的后三步
5.2.1 分析环境变量bootcmd
通过printenv命令可以查看bootcmd的环境变量,发现bootcmd环境变量是运行了bootcmd_stm32mp的程序。通过以下显示结果不难发现,U-boot也是支持Shell语法的。而且启动过程引用了诸多环境变量。
FSMP1A> pri bootcmd
bootcmd=run bootcmd_stm32mp
FSMP1A> pri bootcmd_stm32mp
bootcmd_stm32mp=echo "Boot over ${boot_device}${boot_instance}!";if test ${boot_device} = serial || test ${boot_device} = usb;then stm32prog ${boot_device} ${boot_instance}; else run env_check;if test ${boot_device} = mmc;then env set boot_targets "mmc${boot_instance}"; fi;if test ${boot_device} = nand || test ${boot_device} = spi-nand ;then env set boot_targets ubifs0; fi;if test ${boot_device} = nor;then env set boot_targets mmc0; fi;run distro_bootcmd;fi;
这段代码大致的意思是如果启动设备是serial或usb,就启动stm32prog等待PC机烧录程序。如果不是就设置taboot_target,然后执行run distro_bootcmd。
再使用printenv命令查看distro_bootcmd,不难发现,distro_bootcmd会根据target的值启动Linux系统。
FSMP1A> printenv distro_bootcmd
distro_bootcmd=for target in ${boot_targets}; do run bootcmd_${target}; done
5.2.2 bootcmd_mmc0和bootcmd_mmc1
还可以通过printenv查看bootcmd_mmc0和bootcmd_mmc1的环境变量,继续探究启动细节。
FSMP1A> pri bootcmd_mmc1
bootcmd_mmc1=devnum=1; run mmc_boot
FSMP1A> pri bootcmd_mmc0
bootcmd_mmc0=devnum=0; run mmc_boot
最终可以确定这里执行的命令是bootcmd_mmc0=devnum=0; run mmc_boot;它的意思是设置环境变量devnum为0,然后执行run mmc_boot。
再继续查看run mmc_boot:
pri mmc_boot
mmc_boot=if mmc dev ${devnum}; then devtype=mmc; run scan_dev_for_boot_part; fi
如法炮制,你可以看到很多细节,最终这个启动程序会找到rootfs分区的文件系统,并挂载到内存,然后在boot目录下找到extlinux/extlinux.conf,再根据这个脚本启动Linux系统。
5.3. extlinux.conf脚本分析
关于extlinux.conf脚本的内容可以在进入到Linux系统以后通过cat命令查看文件内容:
# 5.cat /boot/extlinux/extlinux.conf
label stm32mp157a-fsmp1a-buildroot
kernel /boot/uImage
devicetree /boot/stm32mp157a-fsmp1a.dtb
append root=/dev/mmcblk0p4 rootwait
这里可以发现两个关键的文件uImage和stm32mp157a-fsmp1a.dtb。uImage是Linux内核镜像,stm32mp157a-fsmp1a.dtb是内核镜像启动时所需的设备树文件。这两个文件正好对应通用启动流程里提到的"Linux Kernel"和"device tree"。
Linux系统的通用启动流程当中第四步,完全由uImage完成。其中,"挂载根文件系统(rootfs)"则是根据extlinux.conf里的append root=/dev/mmcblk0p4 rootwait完成的。这行代码的意思是挂载位于/dev/mmcblk0p4的文件系统。而/dev/mmcblk0p4的mmcblk0p4正是TF卡。
也就是说Linux系统完成初始化以后会再次(上次是u-boot挂载的文件系统)从TF卡挂载文件系统到内存。完成对用户空间的初始化工作,也就是最后的第五步。
6.第六章 自定义的Linux系统启动方式
6.1. 网络环境配置
以下是使用VMware虚拟机的网络环境配置。如果PC机直接是Ubuntu系统,只要系统具备有线通信可以直接进入设置IP地址的步骤。
6.1.1. 虚拟机是怎样连接到STM32MP157的开发板的?

1-1-1 虚拟机通过有线连接开发板
6.1.2. 虚拟机使用桥接网卡
6.1.2.1 新增虚拟网卡

1-2-1a 为Ubuntu虚拟机新增网卡

1-2-1b 设置网卡为桥接模式
6.1.2.2 桥接到正确的网卡

1-2-2a 打开虚拟网络编程器

1-2-2b 桥接到正确的网卡
PC机本身具备有线网卡的接口,可以桥接到名称带有PCIe字样,如图1-2-2c所示。

1-2-2c 桥接到PCIe网卡
6.1.2.3 扩展坞的连接方式:
如果PC机本身没有有线网卡,可以使用带有网卡功能的扩展坞,一般是USB连到电脑的,所以注意USB字样。如图1-2-3所示。

1-2-3 桥接到外接网卡
6.1.3 设置IP地址
6.1.3.1 设置Ubuntu的IP地址

1-3-1 设置Ubuntu的IP地址
6.1.3.2 设置开发板IP
FSMP1A> setenv ethaddr 00:1B:44:11:3A:B7
FSMP1A> setenv serverip 192.168.9.120
FSMP1A> setenv netmask 255.255.255.0
FSMP1A> setenv ipaddr 192.168.9.210
FSMP1A> setenv gatewayip 192.168.9.1
FSMP1A> saveenv
Saving Environment to MMC... Writing to MMC(0)... OK
FSMP1A>
6.1.3 测试:
FSMP1A> ping 192.168.9.120
6.2. 配置tftp服务器
6.2.1 引入
开发过程当中加载Linux系统:
- 开发过程需要在
host(主机)下进行,而不是嵌入式系统,嵌入式系统一般不具备开发环境。 - 在
host(主机)编译好Linux系统以后,需要把Linux系统下载到target(目标)。 tftp服务器。
6.2.2 什么是tftp?
6.2.2.1 tftp协议
TFTP (Trivial File Transfer Protocol) 是一种简化版的文件传输协议,主要用于在局域网(LAN)中传输小文件。它的设计目标是提供一个简化的、易于实现的文件传输机制,通常用于网络引导和配置设备。
应用层 TFTP
-----------------------------------
传输层:TCP UDP
-----------------------------------
网络层 IP
-----------------------------------
网络接口层
6.2.2.2 TFTP 的特点
简化设计 :TFTP 协议非常简单,没有太多复杂的功能,只支持基本的文件传输操作。
基于 UDP :TFTP 使用 UDP(用户数据报协议)作为传输层协议,这意味着它没有 TCP 提供的可靠性、流量控制和连接管理功能。
无认证机制 :TFTP 不提供用户认证和授权功能,任何能访问服务器的用户都可以传输文件,这使得它在安全性上有所欠缺。
端口号:TFTP 默认使用 UDP 端口 69。
6.2.3. tftp服务的安装与配置
6.2.3.1 安装tftp
sudo apt update;
sudo apt-get install tftpd-hpa tftp-hpa -y
6.2.3.2 修改配置文件
sudo vi /etc/default/tftpd-hpa
修改结果如下:
# 6./etc/default/tftpd-hpa
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/tftpboot"
TFTP_ADDRESS=":69"
TFTP_OPTIONS="--secure"
6.2.3.3 创建所需文件
sudo mkdir /tftpboot;
sudo chmod -R 777 /tftpboot;
6.2.3.4 重启服务
sudo service tftpd-hpa restart
6.2.4 测试
先在tftpboot目录下创建一个1.txt的测试文件,然后到家目录下通过运行tftp服务的方式获取文件。
$ touch /tftpboot/1.txt
$ tftp localhost
tftp> get 1.txt
tftp> quit
$ ls
1.txt Music Public test
6.3. 配置nfs服务器
6.3.1 NFS协议
6.3.1.1 什么是NFS?
NFS(Network File System,网络文件系统)协议是一种允许客户端通过网络访问服务器上文件系统的协议。
6.3.1.2 NFS实现原理
NFS(Network File System,网络文件系统)实现原理包括以下几个关键方面:
-
NFS架构
NFS采用客户端-服务器模型,主要由以下组件构成:
NFS服务器 :提供文件系统并响应客户端请求。
NFS客户端:通过网络访问NFS服务器上的文件系统。+-----------+ Network +-----------+ | NFS Client| <---------------------> | NFS Server| +-----------+ +-----------+ | | | | v v +-----------------+ +---------------------+ | Local Mount | | Shared Directory | | Point /mnt | | /shared_directory | +-----------------+ +---------------------+ -
NFS协议版本
NFSv2 :基于UDP,无状态协议,每个请求独立完成。
NFSv3 :引入对大文件支持,改进错误处理和性能。
NFSv4:基于TCP,有状态协议,增强了安全性和性能,支持文件锁定和ACL(Access Control List,访问控制列表)。
6.3.2 搭建NFS服务器
参考Ubuntu官网,关于NFS文件系统的说明:
https://ubuntu.com/server/docs/network-file-system-nfs
6.3.2.1 安装NFS
sudo apt-get install nfs-kernel-server -y
6.3.2.2 配置NFS环境
-
创建nfs文件系统目录
sudo mkdir -p /nfs/rootfs;
sudo chmod -R 777 /nfs/rootfs; -
修改NFS配置文件
sudo vim /etc/exports
结果如下:
# 6./etc/exports: the access control list for filesystems which may be exported
# to NFS clients. See exports(5).
#
# 6.Example for NFSv2 and NFSv3:
# 6./srv/homes hostname1(rw,sync,no_subtree_check) hostname2(ro,sync,no_subtree_check)
#
# 6.Example for NFSv4:
# 6./srv/nfs4 gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check)
# 6./srv/nfs4/homes gss/krb5i(rw,sync,no_subtree_check)
#
/nfs/rootfs *(rw,sync,no_subtree_check,no_root_squash)
设置完成以后务必重启nfs服务,让配置文件生效
sudo service nfs-kernel-server restart
或者:
sudo systemctl start nfs-kernel-server.service
6.3.3 测试
-
创建所需挂载的目标位置
sudo mkdir -p /mnt/nfs/
-
使用
mount命令将nfs文件系统挂载到目标位置上:sudo mount -t nfs localhost:/nfs/rootfs /mnt/nfs/
-
执行mount如果没有任何打印信息,说明挂载成功啦!此时,如果在
nfs/rootfs/目录下创建一个文件,那么在/mnt/sdcard/目录下也会看到它,以下是打印信息,$ 表示执行的shell命令,其它是执行命令之后的打印信息:mkdir /nfs/rootfs/tmp ls /mnt/nfs/
tmp -
使用
umount取消挂载,取消挂载之后/mnt/nfs/就不会有任何文件了,所以执行ls /mnt/nfs不会打印任何内容cd sudo umount /mnt/nfs
$ ls /mnt/nfs/
6.4. 传统的远程启动方式
6.4.1 设置U-boot环境变量
env default -a
setenv ethaddr 00:1B:44:11:3A:B7
setenv serverip 192.168.9.120
setenv netmask 255.255.255.0
setenv ipaddr 192.168.9.210
setenv gatewayip 192.168.9.1
setenv bootargs root=/dev/nfs nfsroot=192.168.9.120:/nfs/rootfs,proto=tcp,nfsvers=3 rw clk_ignore_unused console=ttySTM0,115200 /init=/linuxrc ip=192.168.9.210
setenv bootcmd tftp 0xc2000000 uImage\;tftp 0xc4000000 stm32mp157a-fsmp1a.dtb\;bootm 0xc2000000 - 0xc4000000
saveenv
6.4.2 复制文件到相应目录
可以将课程资料【固件镜像与根文件系统\rootfs.tar】解压到/nfs/rootfs。结果如下所示:
$ ls /nfs/rootfs/
bin dev lib linuxrc mnt proc rootfs.tar sbin tmp var
boot etc lib32 media opt root run sys usr
根文件系统会随时更新,所以看到的文件可以和这里略有出入。
此时,
TFTP服务器的工作目录下应该保存着uImage和stm32mp157a-fsmp1a.dtb。
$ ls /tftpboot/
stm32mp157a-fsmp1a.dtb uImage
6.4.3 启动Linux系统
如果配置正确,现在重启开发板板就可以看到现象了。
6.4.4 bootargs命令分析
setenv bootargs
root=/dev/nfs
nfsroot=192.168.9.120:/nfs/rootfs,
proto=tcp, <----使用TCP协议
nfsvers=3 <----使用第三个版本
rw <-------------读写权限
clk_ignore_unused <---------忽略未使用的时钟源
console=ttySTM0,115200 <----控制台与波特率
/init=/linuxrc ip=192.168.9.210 <----开发板的IP