- 学习地址:bilibili播主
- 课程名称:《嵌入式之系统移植》-华清远见出品
- 课程链接
20200205~20200209
嵌入式设备的启动
学习新得:以下几点解答了一直困惑的嵌入式设备bootloader存在的位置及启动过程
- bootloader存在的位置大概有了了解,对于使用nor falsh启动的设备,由于nor falsh可以按照字节进行读取,所以在bootloader可以在nor falsh里进行执行,操作系统一般放在另一块存储介质,如nand falsh 或者emmc里;但出于执行效率的考量,一般在前几段bootloader执行完之后,bootloader 会把自己搬移到内存固定位置,然后再初始化更多硬件,然后再把内核搬移到内存,至此把控制权交给内核,内核启动后,加载根文件系统,然后在启动各种应用层应用。
- 如果使用emmc或者nand falsh启动,bootloader会存在emmc或者nand falsh固定位置,上电后,cpu会把bootloader前4k的boot代码(与地址无关的bootloader)搬移掉CPU内部的4K的SRAM中进行执行,做些简单的工作;进入SVC模式,关掉看门狗,关掉中断,关掉MMC,然后做些简单的初始化动作;如初始化时钟,初始化串口,初始化FLASH,初始化内存等,自搬移到内存,设置好栈,进入C语言阶段;然后进大部分的硬件初始化,把FLASH中操作系统内核等全部搬至内存,运行内核,运行设备树文件,挂载根文件系统。
- 三个2操作:两段阶段代码-汇编阶段(需要为C语言建立main栈段),C语言代码阶段;bootloader与位置无关的代码和后续大量与位置有关的代码;两次搬移-自搬移和系统内核搬移;两次初始化-基本硬件初始化和复杂硬件初始化。
- 设备树文件
- 远程根文件系统
bootcmd
:是uboot的启动参数
bootargs
:是ubootl传递给linux内核的启动参数,以字符串形式
Linux内核启动
内核启动流程图
过程说明:
- 一般为了节省空间,一般内核代码(如uImage)要经过压缩,所以存在自解压缩过程
Linux内核调试方法
- 点灯法
- printk打印输出信息方法:在串口初始化完之前会把打印信息缓存在内存里
puts 内核解压之前用
printascii console初始化之前用
printk 内核解压之后,输出信息显示在console初始化之后
打印级别
说明:0级别最高
通过proc在运行时查看和修改日志级别,只需修改一个数字表示级别
cat /proc/sys/kernel/printk 4 4 1 7
echo "7 4 1 7" > /proc/sys/kernel/printk 写入方法
- OOP内核异常信息
根据PC指针的显示,当前出错的函数;再根据交叉编译工具链即可获取出错的代码的位置;
vmlinux是编译后生成的没有经过压缩的内核镜像文件;
c024225c 是出错代码的地方,通过打印的堆栈信息的PC值获取
则可以获取出问题的文件及代码位置
linux内核移植
平台无关的部分
Linux内核配置编译
- 在linux内核源码官网站点下载符合自己需要的内核版本
在自己工作目录解压缩:
tar -xvf linux-4.4.213.tar.xz
- 修改Makefile指定交叉编译工具链
vim Makefile
#ARCH ?= $(SUBARCH)
#CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%)
改为
ARCH ?= arm
CROSS_COMPILE ?= arm-none-linux-gnueabi
- 导入配置eg:
make exynos_defconfig
配置列表见
arch/arm/configs/
目录,找最相似的,此处arm仅是举例,具体CPU具体选择,exynos_defconfig是我们找到符合的配置;
也可采用cp arch/arm/configs/exynos_defconfig .config
的方式导入
- 配置内核:
make menuconfig
- 编译内核:
make uImage
- 编译设备树:
make dtbs
生成的设备树文件在
/arch/arm/boot/dts/
中
dts是描述文件,dtb是生成的可在板子上运行的文件
设备树(DeviceTree):是描述硬件信息的数据结构;用于管理硬件拓扑和硬件资源信息;设备树由一系列被命名的结点(node)和属性(propetry)组成,而结点本身可包含子结点;所谓属性,其实就是成对出现的name和value
在内核源码中的Documentation/devicetree/bindings/
中有简单的说明
网卡驱动移植
make menuconfig
- 配置内核支持网络
- 配置网络协议支持TCP/IP
- 配置支持网络文件系统NFS
- 配置支持(eg:dm9000)网卡驱动(此种情况是内核代码中存在相应的网卡驱动,若没有,要自己编写编译进内核)
平台相关的部分
配置设备树描述硬件和CPU的连接情况
在目录
/arch/arm/boot/dts/
对应的设备树上进行修改
- 修改设备树
可以通过原理图、框图拓扑和CPU等芯片手册知道每个链路、每个设备的地址信息、ID信息
CPU控制外部设备的bank概念和内存控制器的bank概念不是一回事
一般来说,(网卡)驱动程序是与硬件平台无关的,具体(网卡)硬件怎么连接,是由设备树文件说明的;即不同产品对应不同的设备树
对应中断含义可参考Documentation/devicetree/bindings/arm/gic.txt
如下图中的interrupts = <6 4>
中的4的含义-高电平有效
下图以网卡作为举例
- 编译修改后的设备树
make dtbs
;然后使用新生成的设备树.dtb文件
第三方驱动移植
如果内核里已经支持相关驱动,直接选配就可以;如果不支持,就得自己找一个或者开发一个,然后移植到内核
黑盒移植
编译驱动进内核
- 选择驱动存放目录,一般放在
drivers
目录下相关目录(或任意目录)
一般公司级的开发,都会自己创建一个适合本公司的内核驱动目录,用于管理内核驱动源码
- 改Makefile
linux内核源码中每个目录下都有Makefile,负责具体的编译文件的管理
最终编译的时候要在总的Makefile中进行编译;可观察打印信息查看新加的驱动是否编译进来
- 改Kconfig(界面可配置,此方法更加高效好用,且便于管理内核驱动)
linux内核源码中每个目录下都有Kconfig,负责在
menuconfig
界面里显示对应的选项
- 将相应驱动目录下的Makefile和Kconfig做关联
obj-y
是指定在编译时必须编译
obj-$(CONFIG_XXX)
是根据menuconfig的选择情况进行编译
- 编译应用程序用来对驱动进行功能调用进行测试
编译驱动为独立的模块
- 配置为模块方式
menuconfig
中< >
有三个选择,编译或者不编译,以及编程成模块
menuconfig
中[ ]
仅有两个选择,编译或者不编译
是通过Kconfig
中的tristate
关键字表示的
make modules
编译为模块
编译完之后的模块化的内核驱动文件后缀是
.ko
;产品可以选择动态的加载该驱动模块
insmod xxx.ko
Linux提供的动态加载驱动到内核模块的命令
mknod
创建设备结点 eg:mknod dev/led c 501 0
(c字符设备、501主设备号、0副设备号)
内核驱动程序都是被动运行的,由上层应用触发驱动模块三要素:加载入口:
module_init()
卸载入口:module_exit()
免费开源声明:MODULE_LICENSE()
- 创建设备结点(应用访问驱动的入口)
mknod
创建设备结点
- 运行测试驱动的应用程序
白盒移植
打印跟踪
驱动框架
字符设备
- 内核设备驱动框架
- 硬件设备层:每个设备都有一个设备号
- 内核层:驱动框架要对每个设备进行注册设备号
- 应用层:
注册设备号->初始化该设备->操作设备
内核需要使用
ioremap
内核函数将芯片手册提供的硬件设备的物理地址转换成内核可以操作的地址
平台设备
- 解决了什么问题
硬件设备更新后,减少驱动更改的代码量;
因为驱动程序里面包含具体设备的物理地址等信息,如果硬件设备设计更新,一般来说都要修改内核驱动代码
- 平台设备驱动框架
怎么做的:把硬件信息从驱动里剥离出来,放到设备树文件当中,这样让驱动程序间接的获取设备树文件中的硬件信息,硬件变化一般只要更改设备树文件就可以了,概括地说就是分离概念,好处便于维护
根文件系统
根文件系统概念
根文件系统作用
根文件系统(root filesystem)是存放运行、维护系统所必需的各种工具软件、库文件、脚本、配置文件和其它特殊文件的地方(载体仓库),也可以安装各种软件包;是内核起来之后挂载的第一个文件系统,之后还可以挂载其它文件系统
可以使用busybox工具制作根文件系统
设备访问
主设备号和次设备号
- Linux系统是通过主设备号和次设备号来区分设备的
主设备号(major):系统内核用来区分哪类设备
次设备号(minor):区分某类设备中的哪个设备
可在内核的文档中看到:Documentation/devices.txt
创建设备结点
设备文件不能在加载驱动程序的时候自动创建,要通过指令创建
- 创建设备文件的一般语法
mknod /dev/<device> [c|b] <major> <minor>
eg:
mknod /dev/ttySAC0 c 4 64
mknod /dev/hda1 b 3 1
Linux系统的引导过程
init是系统起来之后工作的第一个应用程序
根文件系统的制作
制作根文件系统的内容
- 采用busybox创建基本命令
- 创建基本的目录
/lib /etc /var /tmp /dev /sys /proc
等 - 添加glibc基本动态库
- 创建基本的设备结点
- 添加启动配置和脚本程序
/etc/inittab
、/etc/fstab
、/etc/init.d/rcS
、
测试rootfs文件系统的正确性
制作需要的rootfs的类型的格式
不同形式的根文件系统