嵌入式系统的部署和启动都是参考PC机的?

目录

目录

概念机器的启动过程

计算机系统就是有 CPU 来做核心进行运行的系统。典型的计算机系统有:PC 机(台式机+笔记本)、嵌入式设备(手机、 平板电脑、 游戏机) 、 单片机(家用电器像电饭锅、空调)。计算机系统的组成部件非常多,不同的计算机系统组成部件也不同。 但是所有的计算机系统运行时需要的主要核心部件都是 3 个东西: CPU + 外部存储器( Flash/硬盘) + 内部存储器( DDR SDRAM/SDRAM/SRAM) 。

典型的 PC 机的部署: BIOS 程序部署在 PC 机主板上(随主板出厂时已经预制了) , 操作系统部署在硬盘上, 内存在掉电时无作用, CPU在掉电时不工作。

启动过程: PC 上电后先执行 BIOS 程序(实际上 PC 的 BIOS 就是 NorFlash) , BIOS 程序负责初始化 DDR 内存, 负责初始化硬盘, 然后从硬盘上将 OS 镜像读取到 DDR 中, 然后跳转到 DDR中去执行 OS 直到启动(OS 启动后 BIOS 就无用了)。

嵌入式系统的部署和启动都是参考 PC 机的。 只是设备上有一些差别。 典型嵌入式系统的部署: uboot 程序部署在 Flash(能作为启动设备的 Flash) 上、 OS 部署在 Flash(嵌入式系统中用 Flash代替了硬盘) 上、 内存在掉电时无作用, CPU 在掉电时不工作。

启动过程:嵌入式系统上电后先执行 uboot、 然后 uboot 负责初始化 DDR, 初始化 Flash, 然后将 OS 从 Flash 中读取到 DDR 中, 然后启动 OS(OS 启动后 uboot 就无用了)

总结: 嵌入式系统和 PC 机的启动过程几乎没有两样, 只是 BIOS 成了 uboot, 硬盘成了 Flash。

android 系统的启动和 linux 系统(前面讲的典型的嵌入式系统启动) 几乎一样, 只是在内核启动后加载根文件系统后不同了。 可以认为启动分为 2 个阶段:第一个阶段是 uboot 到 OS 启动; 第二个阶段是 OS 启动后到 rootfs 加载到命令行执行。

什么是 uboot

uboot 就是 universal bootloader(通用的启动代码) ,通用的意思就是在各种地方都可以用。所以说 uboot 具有可移植性。 uboot 具有可移植性并不是说 uboot 在哪个开发板都可以随便用, 而是说 uboot 具有在源代码级别的移植能力,可以针对多个开发板进行移植,移植后就可以在这个开发板上使用了。

uboot 的本质是一个裸机程序,和我们裸机全集中写的那些裸机程序 xx.bin 并没有本质区别。如果非说要有区别,那就是:我们写的大部分小于 16KB,而 uboot 一般 uboot 在 180k-400k 之间。uboot 本身是一个开源项目,由若干个.c 文件和.h 文件组成,配置编译之后会生成一个 u-boot.bin,这就是 uboot 这个裸机程序的镜像文件。然后这个镜像文件被合理的烧录到启动介质中拿给 SoC 去启动。也就是说 uboot 在没有运行时表现为 uboot.bin,躺在启动介质中。运行时会被加载到内存中然后一条指令一条指令的拿给 CPU 去运行。

uboot 主要作用是用来启动操作系统内核、负责部署整个计算机系统、操作 Flash 等板子上硬盘的驱动、提供一个命令行界面供人来操作。

uboot 的入口就是开机自动启动, uboot 的唯一出口就是启动内核。 uboot 还可以执行很多别的任务(譬如烧录系统),但是其他任务执行完后都可以回到 uboot 的命令行继续执行 uboot 命令,而启动内核命令一旦执行就回不来了。

uboot 必须解决哪些问题

自身可开机直接启动: 一般的 SoC 都支持多种启动方式,譬如 SD 卡启动、NorFlash 启动、NandFlash 启动等,uboot 要能够开机启动,必须根据具体的 SoC 的启动设计来设计 uboot。uboot 必须进行和硬件相对应的代码级别的更改和移植,才能够保证可以从相应的启动介质启动。uboot 中第一阶段的 start.S 文件中具体处理了这一块。

能够引导操作系统内核启动并给内核传参: uboot 的终极目标就是启动内核。

能提供系统部署功能: uboot 必须能够被人借助而完成整个系统(包括 uboot、 kernel、 rootfs 等的镜像) 在 Flash 上的烧录下载工作。 例如利用 uboot 中的 fastboot 功能将各种镜像烧录到 iNand中, 然后从 iNand 启动。

能进行 soc 级和板级硬件管理:uboot 中实现了一部分硬件的控制能力(uboot 中初始化了一部分硬件) ,因为 uboot 为了完成一些任务必须让这些硬件工作。譬如 uboot 要实现刷机必须能驱动 iNand,譬如 uboot 要在刷机时 LCD 上显示进度条就必须能驱动 LCD,譬如 uboot 能够通过串口提供操作界面就必须驱动串口。譬如 uboot 要实现网络功能就必须驱动网卡芯片。SoC 级(譬如串口)就是 SoC 内部外设,板级就是 SoC 外面开发板上面的硬件(譬如网卡、 iNand)

uboot 命令和环境变量

uboot 启动后大部分时间和工作都是在 shell 下完成的(譬如 uboot 要部署系统要在 shell 下输命令、 要设置环境变量也得在命令行下,要启动内核也要在命令行底下敲命令)

命令就是 uboot 的 shell 中可以识别的各种命令。 uboot 中有几十个命令。不同 uboot 的命令不一定相同。我们还可以自己给 uboot 添加命令。

uboot 的环境变量和操作系统的环境变量工作原理和方式几乎完全相同。uboot 在设计时借助了操作系统的设计理念。环境变量可以被认为是系统的全局变量,环境变量名都是系统内置的(认识就认识, 不认识就不认识,这部分是系统自带的默认的环境变量,譬如 PATH;但是也有一部分环境变量是自己添加的,自己添加的系统就不认识但是我们自己认识)。系统或者我们自己的程序在运行时可以通过读取环境变量来指导程序的运行。这样设计的好处就是灵活,譬如我们要让一个程序更改运行方法,不用去重新修改程序代码再重新编译运行,而只要修改相应的环境变量就可以了。环境变量就是运行时的配置属性。

环境变量就好像程序的全局变量一样。程序中任何地方都可以根据需要去调用或者更改环境变量(一般都是调用 ,环境变量和全局变量不同之处在于:全局变量的生命周期是在程序的一次运行当中,开始运行时诞生程序结束时死亡,下次运行程序时从头开始;但是环境变量被存储在Flash 的另一块专门区域(Flash 上有一个环境变量分区),一旦我们在程序中保存了该环境变量,那么下次开机时该环境变量的值将维持上一次更改保存后的值。

Uboot 重定位

Uboot 源码中一个比较重要的概念是“重定向”(relocate),简单来说就是将 u-boot 的运行环境转移到 SDRAM 中去。

通常来说,当 Uboot 运行于 DDR 时,无需重定向就可以运行起来。当 U-Boot 在 NorFlash、MMC、SPI Flash 等作为启动设备的存储介质中时,SoC 内部的 RAM 空间通常无法满足U-Boot的需求,此时,需要 Uboot 自身通过重定向功能实现程序的搬移。

uboot工作流程

①寻找程序入口

②第一阶段程序(BL1)分析

③第二阶段程序(BL2)分析

以2440为例子作为说明。

寻找程序入口:可通过查看链接文件得知。对于三星的 smdk2440 ,从 u-boot.lds 文件中可以看到,start.o 位于整个代码的最前端,并且 ENTRY(_start) 说明程序入口为 _start,而 start.o 对应的程序为 start.S,因此 start.S 中的 _start 为 uboot 程序的入口

BL1 代码分析(只分析从 nand flash 启动时代码做的工作)

(1)设置中断向量表

(2)设置处理器为 svc 模式

(3)刷新 I/D cache

(4)关闭 mmu 和 cache

(5)关闭看门狗

(6)关闭所有中断

(7)初始化系统时钟

(8)初始化串口

(9)简单初始化 nand flash

(10)进行内存初始化

(11)复制 nand flash 中的 bl 到内存

(12)设置堆栈

(13)清除 bss 段

(14)跳转到 start_armboot(start_armboot 为 BL2 的程序入口)

BL2

(1)初始化串口

(2)LCD 初始化

(3)初始化网卡

(4)初始化 LED

(5)执行用户输入的命令(死循环)

Uboot 命令信息查询命令bdinfo

功能:直接输入 bdinfo 查看板子信息:

arch_number = 0x00000000 //架构编号
boot_params = 0x80000100 //启动参数保存起始地址
DRAM bank = 0x00000000
-> start = 0x80000000 //DRAM bank 的起始地址-> size = 0x20000000 //DRAM bank 的长度
eth0name = FEC1 //网卡名字
ethaddr = 00:04:9f:04:d2:35
current eth = FEC1 //当前使用的网卡
ip_addr = 192.168.1.20
baudrate = 115200 bps //串口波特率
......

printenv

功能:输出环境变量信息

说明: uboot 按下 TAB 键会自动补全命令, 也可只输入 pri 或者 print。

version

功能:查看 uboot 的版本号:

U-Boot 2016.03-gd9420c3 (Nov 01 2019 - 12:03:59 +0800) 	   # uboot 版本号为 2016.03
arm-poky-linux-gnueabi-gcc (GCC) 5.3.0 			   # 编译器为 arm-linux-gnueabi-gcc
GNU ld (GNU Binutils) 2.26.0.20160214

环境变量操作命令setenv /set

功能:命令 setenv 用于设置或者修改环境变量的值。

说明:有时候我们修改的环境变量值可能会有空格,比如 bootcmd、 bootargs 等,这个时候环境变量值就得用单引号括起来,比如下面修改环境变量 bootcmd 的值:

setenv bootcmd 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
setenv author zuozhongkai 					# 新建环境变量
setenv author 								# 删除环境变量:参数为空即可

saveenv /save

功能:保存环境变量的更改。

说明:命令不带参数,直接执行,作用是将内存中的环境变量的值同步保存到 Flash 中环境变量的分区。 如果只 set 不 save, 那 set 后就只是当前本次运行的 uboot 有效。

内存操作命令

内存操作命令就是用于直接对 DRAM 进行读写操作。

md

功能: md 就是 memory display, 用于显示内存值。使用方法如下:

格式:md[.b, .w, .l] address [# of objects]
示例:md.b 80000000 14 				# 查看以 0X80000000 开始的 20 个字节的内存值

nm 和 mm

功能: 都是用于修改指定地址的内存值

格式:nm [.b, .w, .l] address

输入命令后,在弹出的命令行中输入新值,可重复修改,按 q 退出。 nm 只能修改指定一个地址的值,而 mm(即 memory modify) 修改后会自动加地址。

mw

功能: mw 就是 memory write, 将内容填充到内存中

格式:mw [.b, .w, .l] address value [count]
示例:mw.l 80000000 0A0A0A0A 10					#给指定个每个地址都填充一样的数。

cp

功能: 用于将 DRAM 中的数据从一段内存拷贝到另一段内存中.

格式:cp [.b, .w, .l] source target count
示例:cp.l 80000000 80000100 10

source 为源地址, target 为目的地址, count 为拷贝的长度。

cmp

功能: 比较命令, 用于比较两段内存的数据是否相等。

格式:cmp [.b, .w, .l] addr1 addr2 count
示例:cmp.l 80000000 80000100 10

addr1 为第一段内存首地址, addr2 为第二段内存首地址, count 为要比较的长度。

网络操作命令相关环境变量ipaddr 开发板的本地 IP 地址ethaddr 是开发板的本地网卡的 MAC 地址。serverip 是开发板通过 tftp 指令去 tftp 服务器下载东西时, tftp 服务器的 IP 地址。netmask 子网掩码gatewayip 网关地址dhcp

功能: 用于从路由器获取 IP 地址, 前提得开发板连接到路由器上的, 如果开发板是和电脑直连的, 那么 dhcp 命令就会失效。

nfs

功能: 在计算机之间通过网络来分享资源。

格式:nfs [loadAddress] [[hostIPaddr:]bootfilename]
示例:nfs 80800000 192.168.1.141:/home/gec/linux/nfs/zImage

loadAddress 是要保存的 DRAM 地址, [[hostIPaddr:]bootfilename]是要下载的文件地址。

tftp

功能: 同nfs 命令一样, 都是用于通过网络下载东西到 DRAM 中

示例:tftp 80800000 zImage

Ubuntu 主机作为 TFTP 服务器。 需要搭建 TFTP 服务器和安装 tftp-hpa 和 tftpd-hpa 服务。

Flash 操作命令

uboot 支持 EMMC 和 SD 卡, 因此也要提供 EMMC 和 SD 卡的操作命令。 一般认为 EMMC 和 SD 卡是同一个东西, uboot 中常用于操作 EMMC 设备的命令为“mmc”。

开发板如果用 SD 卡/EMMC/iNand 等作为 Flash,则在 uboot 中操作 flash 的指令为 movi(或mmc)。

movi

movi 指令是一个命令集,有很多子命令,具体用法可以 help movi 查看。

movi read {u-boot | kernel} {addr} 		# 读取 iNand 到 DDR 上
movi write								# 将 DDR 中的内容写入 iNand 中

示例:

movi read u-boot 0x30000000				

把iNand中的 u-boot 分区读出到 DDR 的 0x30000000 起始的位置处。uboot 的命令行中所有数字都被默认当作十六进制处理,这里的 0x30000000 也可以直接写作30000000。

mmc info / mmcinfo

功能: 用于输出当前选中的 mmc info 设备的信息:

Device: FSL_SDHC
Manufacturer ID: 15
OEM: 100
Name: 8GTF4
Tran Speed: 52000000
Rd Block Len: 512
MMC version 4.0 				  # MMC 版本为 4.0
High Capacity: Yes
Capacity: 7.3 GiB 				  # 容量为 7.3Gib
Bus Width: 8-bit 				  # 8 位宽的总线
Erase Group Size: 512 KiB

mmc list

功能: 用于来查看当前开发板一共有几个 MMC 设备

FSL_SDHC: 0 						# FSL_SDHC:0 是 SD 卡
FSL_SDHC: 1 (eMMC)

mmc dev

功能: 切换当前 MMC 设备

格式:mmc dev [dev] [part]
示例:mmc dev 0 					   # 切换到 SD 卡, 0 为 SD 卡, 1 为 eMMC
	 mmc dev 1 2 					 # 切换到 EMMC 的分区 2

mmc part

功能: 查看当前 MMC 设备的分区:

$ mmc part 
Partition Map for MMC device 1 -- Partition Type: DOS
Part Start Sector Num Sectors UUID 			Type
1 		2048 		65536 	  e5788bc5-01 	0c Boot
2 		67584 		15202304  e5788bc5-02 	83

说明: 可以看出, 此时 EMMC 有两个分区, 扇区 2048~65536 为第一个分区, 扇区67584~15202304 为第二个分区。 第 0 个分区没有格式化所以不显示。

mmc read

功能: 用于读取 mmc 设备的数据。

格式:mmc read addr blk cnt
示例:mmc read 80800000 600 10 		# 读取数据

mmc write

功能: 将数据写到 MMC 设备里面。

格式:mmc write addr blk cnt

addr 是数据在 DRAM 中的起始地址, blk 是要写入 MMC 的块起始地址, cnt 是要写入的块大小, 一个块为 512 字节。 我们可以使用命令“mmc write”来升级 uboot。 注意千万不要写 SD卡或者 EMMC 的前两个块(扇区), 里面保存着分区表。

BOOT 操作指令bootz

功能: bootz 命令用于启动 zImage 镜像文件和设备树文件。

格式:bootz [addr [initrd[:size]] [fdt]]
示例:tftp 80800000 zImage 						# Linux 镜像
	 tftp 83000000 imx6ull-alientek-emmc.dtb 	  # 设备树
	 bootz 80800000 – 83000000 				  	  # 启动 LInux 系统

addr 是 Linux 镜像文件在 DRAM 中的位置, initrd 是 initrd 文件在 DRAM 中的地址,如果不使用 initrd 的话使用 “-” 代替即可, fdt 就是设备树文件在 DRAM 中的地址。

bootm

功能: bootz 功能类似, 用于启动 uImage 镜像文件和设备树文件。

格式:bootm addr 							# 启动 uImage 镜像文件,不使用设备树
	 bootm [addr [initrd[:size]] [fdt]]   # 使用设备树

addr 是 uImage 镜像在 DRAM 中的首地址。 initrd 是 initrd 的地址, fdt 是设备树(.dtb)文件。 在 DRAM 中的首地址, 如果 initrd 为空的话, 同样是用“-” 来替代。

bootdelay 和 bootcmd 和 bootargsbootdelay: uboot 开启后启动内核的倒计时。无打断则执行 bootcmd 所对应的命令集。bootargs : 保存着 uboot 传递给 Linux 内核的参数。bootcmd : 保存着 uboot 默认命令,用来启动 Linux 内核。linux 内核启动时可以接收 uboot 给他传递的启动参数,这些启动参数是 uboot 和内核约定好的形式、内容,linux 内核在这些启动参数的指导下完成启动过程。这样的设计是为了灵活,为了内核在不重新编译的情况下可以用不同的方式启动。在 uboot 的环境变量中设置 bootargs,然后 bootm 命令启动内核时会自动将 bootargs 传给内核。

bootargs=console=ttySAC2,115200 root=/dev/mmcblk0p2 rw init=/linuxrc rootfstype=ext3

(1)console=ttySAC2,115200 : 控制台使用串口 2, 波特率 115200.

(2)root=/dev/mmcblk0p2 rw : 根文件系统在 SD 卡端口 0 设备(iNand) 第 2 分区, 可读写

(3)init=/linuxrc : linux 的进程 1(init 进程) 的路径

(4)rootfstype=ext3 : 根文件系统的类型是 ext3

其他命令reset

复位, 输入“reset”即可复位重启。

go

用于跳到指定的地址处执行应用, 命令格式: go addr [arg …]

run

用于运行环境变量中定义的命令, 如 run bootcmd

mtest

内存读写测试命令, 如 mtest 80000000 80001000: 测试 0X80000000~0X80001000 这段内存

© 版权声明
THE END
喜欢就支持一下吧
点赞126赞赏 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容