系统移植11111

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.实验原理:

  • LinuxU盘、TF卡等存储设备抽象为/dev下的块设备节点,通过节点完成硬件读写。
  • ddLinux底层读写工具,实现文件与块设备原始拷贝;配置 ifofbs、同步缓存等参数,可将镜像逐块烧录到TF卡;nocreatfsync等参数防止写入异常、缓存丢失,保证烧录可靠。
  • STM32MP1依靠拨码开关设置BOOT引脚电平选择启动介质;拨码设为101,开发板从TF卡加载镜像,完成初始化并启动Linux

什么是烧录? 就是把编译好的系统镜像写入存储硬件,烧录是嵌入式设备部署系统的前置步骤。

2.实验步骤:

第一步 识别TF卡到Ubuntu系统

第二步 使用dd命令烧录镜像文件

第三步 启动STM32MP1使其进入到Linux系统

2.实验说明:

实验当中以$开头的代码代表其后是在shell下运行的命令

2.2. 识别TF卡到Ubuntu系统

2.2.1 识别设备到Ubuntu

通过读卡器或者带插卡槽的扩展坞,将TF卡连到PC机上。如果你使用的UbuntuVMware的虚拟机的话,会看到如图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 存储设备状态图标的右击菜单

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

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开头+az小写字母的方式命名,如sdasdb等。一般最后一个块设备就是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系统里。如果你使用的UbuntuVMware的虚拟机,可以先把文件下载到宿主机。然后,通过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) 权限执行。

ifof,即input fileoutput 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 开关上的01

4-3-1 拨码开关的0和1

2.4.3.2 拨码开关和BOOT引脚

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

4-3-2 拨码开关与Boot引脚

2.4.3.3 原理总结

根据以上分析,手动改变拨码开关的01,会直接影响SoCBOOT引脚的01SoC会根据BOOT引脚的值选择不同的启动方式。BOOT引脚和启动方式对应关系如图所示:

4-3-3a 电路原理图当中关于BOOT引脚与启动方式说明

其中,010eMMC启动、101SD-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

可以看到镜像文件有fsbl1fsbl2ssblrootfs四个分区。

复制代码
┌───────┬───────┬──────┬────────────────────────────────────┐
│ fsbl1 │ fsbl2 │ ssbl │              rootfs                │
└───────┴───────┴──────┴────────────────────────────────────┘
                     (sdcard.img的分区信息)

3.1.3 文件系统

其中,rootfs分区存在一个文件系统 。所谓文件系统,就是块设备上数据存储的一套规则。本质上就是按照某种规则存储在块设备上的数据。有了这个数据以后就可以通过cdlspwd等常见的普通文件相关的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-AU-bootTF-AU-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位,最大寻址约8GBLBA48位,可达128PB,提升约1600万倍。

现代硬盘扇区实际容量也进行了升级。从原来的512字节升级为4KB4096字节)。传统512字节扇区附带校验等额外开销,硬盘容量越大,开销占比越高;改用4KB物理扇区能够减少开销,提升空间利用率。

Linux系统等上层软件仍沿用512字节扇区的逻辑。为实现兼容,硬盘对外接口模拟512字节扇区:上层读写512字节时,硬盘内部实际读取完整4KB物理扇区,再返回对应512字节数据。即上层看到的是逻辑512B扇区,硬盘真实物理扇区为4KB

知识点总结:扇区是存储设备的最小的存储单元;每个扇区的大小为512B(尽管底层硬件的实现已经不是这样)。

3.2.2 分区和分区表

分区指将存储设备的存储空间划分成逻辑上独立的几个部分,每个部分就是一个独立的分区。例如Windows下的C盘、D盘等,就是一个个的分区,Linux下的sda1sda2等,也是一个个的分区。

分区的种类和一些实现细节,由于篇幅原因,加之课程只是对存储设备进行简单分区,所以这里不再讨论关于分区 的具体细节,只对进行分区时可能会遇到的概念进行讲解。

需要了解的分区是MBRGPT分区两类;

3.2.2.1. MBR分区

MBR分区是传统的分区方式。MBR分区的分区的数量受限,而且单个分区的大小也不能超过2TB。所以需要另外一种分区------GPT

GPTGUID Partition Table)分区支持2TB以上的分区,分区的数量也不受限制(但分区数量受操作系统的限制)。

不管采用哪种分区形式,都需要在在硬盘上保存分区信息,这个分区信息就是分区表。不同的分区形式的分区表在硬盘上的位置和数据结构都会有所不同。在进行分区时不必考虑这些细节,因此这里不再赘述。

3.3.复刻一个新的Linux系统镜像

实验目的 : 建立对系统镜像文件建立感性认知,理解SD卡镜像内部GPT分区布局,掌握loop虚拟块设备用法,理解镜像从01的生成流程,区分原始镜像文件与真实物理TF卡块设备的异同。

实验原理 : 使用前面学到的dd命令生成全0空白镜像文件,配合sgdisk命令完成GPT分区表初始化,借助Linux loop设备将普通文件模拟成块设备,分别写入TF‑AU‑Boot启动固件,为rootfs分区创建ext4文件系统并填充根文件系统内容,最终得到一份可以直接烧录到TF卡运行的完整sdcard镜像文件

实验步骤

  1. dd命令生成镜像文件
  2. 使用parted命令和sgdisk命令完成分区信息的初始化
  3. 通过loop设备烧录TF-AU-boot
  4. 创建文件系统并写入系统文件

实验说明 :实验当中以$开头的代码代表其后是在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分区操作,sgdiskGPT支持更加友好,本实验优先使用sgdisk。此时镜像文件已经具备GPT分区表,但分区内全部还是0,没有固件、没有文件系统。

3.3.3 通过loop设备烧录TF-AU‑Boot固件

loop设备可以把普通.img文件虚拟成一个块设备,就如同真实TF/dev/sdb,系统会自动生成对应的 loop分区设备/dev/loopXp1/dev/loopXp2/dev/loopXp3/dev/loopXp4,分别对应 fsbl1fsbl2ssblrootfs四个分区。

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固件写入fsbl1fsbl2分区

fsbl1fsbl2存放完全相同的TF‑AATF)固件,双分区设计用于固件容错升级。

课程资料中【固件镜像与根文件系统】准备好tf‑a.stm32文件。使用dd写入loop0p1loop0p2

复制代码
$ 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

此时fsbl1fsbl2ssbl三个裸数据分区已经写入完成。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.imgTF卡测试

按照实验 1 的步骤,识别 TF 卡块设备,执行 dd 烧录:

复制代码
$ sudo dd if=sdcard.img of=/dev/sdb bs=4M conv=nocreat,fsync status=progress

/dev/sdb 替换成你的 TF 卡设备节点,千万不要写错设备,避免破坏本地硬盘

烧录完成,开发板拨码开关设置为101SD‑Card启动),上电,串口观察打印。如果复刻成功,可以正常输出U‑Boot日志,最终输出fsmp1a login:登录提示符,输入root登录系统。

4.第四章 嵌入式Linux系统的启动流程

4.1. 前置知识

4.1.1 ROM和RAM

  1. ROM(只读存储器) :掉电数据不丢失;数据写入后一般不可修改。 分类:
    • ROM:出厂固化,不可改写,成本低,用于开机启动。
    • PROM:用户只能编程写入1次。
    • EPROM:可多次擦写改写。
    • EEPROM:原理同EPROM,擦除操作更简便。
  2. RAM(随机存取存储器) :读写速度和存储位置无关,掉电数据丢失
    • SRAM(静态RAM):速度快、价格高,用作高速缓存Cache。
    • DRAM(动态RAM):用作计算机主内存。
  3. Flash Memory(闪存) :高密度非易失存储器,兼具EEPROMRAM特性,主流分为Nor Flash、Nand Flash:
    • Nor Flash:支持字节寻址,可芯片内直接运行程序(XIP);读快、写慢。嵌入式常用存启动代码。
    • Nand Flash:串行块读写,不能XIP;存储密度高、成本低。用于存放普通数据。

历史:1988Intel推出Nor Flash1989年东芝推出Nand Flash。 嵌入式常见搭配:小容量Nor Flash放启动代码,Nand Flash存放其余数据。

4.1.2. bootloader

集成电路只读存储器问世前,早期ENIAC计算机没有存储程序,依靠接线配置完成运算,不存在自举电路、引导程序,也没有BootLoaderBoot概念。

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运行模式、内部时钟,以及CacheMMU等。

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 CodeST官方在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 所示,就是数据手册里的SYSRAMROM。之所以需要初始化外部的RAM,是由于SSBL的大小一般会超过256KB,他们会被加载到外置的RAM当中。

2-3-1 数据手册里的ROM和SYSRAM

4.2.4 外置的RAM与后续的启动流程

STM32MP157开发板上的外置RAM是一块大小为4Gb,即512MBDDR RAM。图2-4-1是STM32MP157开发板的设计者提供的DDR的规格书的首页,规格书当中标明了DDR的大小为4Gb,这里是小写b,代表bit,大写B代表byte,字节。所以STM32MP157开发板上搭载的是一块大小为512MBDDR RAM

2-4-1 DDR的规格书首页截图

总体上,我们可以将以上的启动流程总结为从开发板上电开始,CPU会自动执行ROM codeROM code会将FSBL加载到内置的RAM,这个内置的RAMSTM32MP157A上就是SYSRAMFSBL启动之后再将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-booSSBL。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‑BootUniversal Bootloader,通用引导加载程序),是嵌入式领域最主流的开源二级引导程序(SSBL)。

值得注意的是,TF-AOP-TEE运行在SYSRAM当中,而U-bootLinux运行在外置的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;

这段代码大致的意思是如果启动设备是serialusb,就启动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_mmc0bootcmd_mmc1

还可以通过printenv查看bootcmd_mmc0bootcmd_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;它的意思是设置环境变量devnum0,然后执行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

这里可以发现两个关键的文件uImagestm32mp157a-fsmp1a.dtbuImage是Linux内核镜像,stm32mp157a-fsmp1a.dtb是内核镜像启动时所需的设备树文件。这两个文件正好对应通用启动流程里提到的"Linux Kernel"和"device tree"。

Linux系统的通用启动流程当中第四步,完全由uImage完成。其中,"挂载根文件系统(rootfs)"则是根据extlinux.conf里的append root=/dev/mmcblk0p4 rootwait完成的。这行代码的意思是挂载位于/dev/mmcblk0p4的文件系统。而/dev/mmcblk0p4mmcblk0p4正是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系统:

  1. 开发过程需要在host(主机)下进行,而不是嵌入式系统,嵌入式系统一般不具备开发环境。
  2. host(主机)编译好Linux系统以后,需要把Linux系统下载到target(目标)。
  3. 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,网络文件系统)实现原理包括以下几个关键方面:

  1. NFS架构
    NFS采用客户端-服务器模型,主要由以下组件构成:
    NFS服务器 :提供文件系统并响应客户端请求。
    NFS客户端:通过网络访问NFS服务器上的文件系统。

    复制代码
        +-----------+          Network         +-----------+
        | NFS Client|  <---------------------> | NFS Server|
        +-----------+                          +-----------+
             |                                      |
             |                                      |
             v                                      v
    +-----------------+                 +---------------------+
    | Local Mount     |                 | Shared Directory    |
    | Point /mnt      |                 | /shared_directory   |
    +-----------------+                 +---------------------+
  2. 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环境
  1. 创建nfs文件系统目录

    sudo mkdir -p /nfs/rootfs;
    sudo chmod -R 777 /nfs/rootfs;

  2. 修改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 测试

  1. 创建所需挂载的目标位置

    sudo mkdir -p /mnt/nfs/

  2. 使用mount命令将nfs文件系统挂载到目标位置上:

    sudo mount -t nfs localhost:/nfs/rootfs /mnt/nfs/

  3. 执行mount如果没有任何打印信息,说明挂载成功啦!此时,如果在nfs/rootfs/目录下创建一个文件,那么在/mnt/sdcard/目录下也会看到它,以下是打印信息,$ 表示执行的shell命令,其它是执行命令之后的打印信息:

    mkdir /nfs/rootfs/tmp ls /mnt/nfs/
    tmp

  4. 使用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服务器的工作目录下应该保存着uImagestm32mp157a-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

6.5. 传统的本地启动方式

相关推荐
这张生成的图像能检测吗3 小时前
(论文速读)GMA-Net:面向嵌入式物联网设备的航空发动机损伤检测轻量级实时网络
物联网·目标检测·缺陷检测·轻量化·设备部署
wtblszn5 小时前
光伏封装产线智能管理平台方案
网络·物联网
星恒讯工业路由器5 小时前
分布式光伏站点分散难联网?4G/5G点对多点自组网方案解析
分布式·物联网·5g·分布式光伏·自组网·点对多点通信·4g/5g
zcmodeltech6 小时前
智慧矿山沙盘模型多场景控制系统设计——基于STM32与Modbus RTU的智能开采、无人矿卡、数字孪生全场景联动方案
数据库·stm32·单片机·嵌入式硬件·物联网
2601_953988077 小时前
Ricon组态实时监控 - 毫秒级数据可视化
前端·物联网·数学建模·信息可视化·架构·前端框架
云道轩8 小时前
分析IBM Maximo Monitor 和thingboard在物联网功能上的异同
物联网
h6j7k8l9p09 小时前
蓝牙模块的“人性化”设计:从用户故事看HC-05的易用性与常见陷阱
物联网·蓝牙模块·创客教育·hc-05
TDengine (老段)9 小时前
TDgpt 概览 — AI 增强的时序数据库
大数据·数据库·人工智能·物联网·时序数据库·iot·tdengine
数字新视界19 小时前
2026模块化机房选型指南:行业市场发展趋势、占有率与竞争梯队分析报告解析
大数据·人工智能·物联网·数据中心·微模块机房·模块化机房·冷通道