嵌入式系统移植
一、嵌入式系统
- 一般定义
以应用为中心、以计算机技术为基础、软件硬件可裁剪、适应应用系统,对功能、可靠性、成本、体积、功耗严格要求的专用计算机系统。 - 广义上讲
凡是带有微处理器的专用软硬件系统都可称为嵌入式系统。 - 嵌入式系统的核心是定制
是根据市场需求对软硬件进行定制,实现性能的最大化
通用嵌入式系统软件组成部分
- 图一为无os嵌入式系统组成图
- 图二为有os嵌入式系统组成图
嵌入式Linux内核结构
Android系统
二、嵌入式开发环境搭建
开发板启动流程
嵌入式启动流程
板子上电后首先运行bootloader,这是上电后的第一个程序,接着初始化硬件,引导加载内核,内核起来之后,挂载我们的rootfs根文件系统,接着运行我们的程序。
搭建嵌入式Linux开发环境的主要工作
- 准备开发主机、目标机(开发板)以及二者的连接介质
- 准备目标机代码
- 安装交叉工具链
- 开发主机上安装的软件(为方便调试)
- 终端软件(putty、minicom)
- tftp服务
- nfs服务
- 目标机安装(u-boot烧写调试)
SD卡方式
Fastboot方式 (成功后,板子有串口信息输出) - 主机和目标机能联通
- 网络自动 tftp 加载内核,并挂载nfs rootfs 启动
连接TFTP与NFS服务nfs与tftp服务搭建
使用条件是:
需要网络连接
主机端需要安装 tftp 服务器软件
目标机需要实现tftp客户端(开发板已经装好,因为开发板上有uboot,uboot包含tftp客户端)
优点:
传输速度快
可以将编译好的内核下载到目标机,提高开发效率
tftp 是用来下载远程文件的最简单网络协议,它基于udp协议而实现。嵌入式 linux 的 tftp 开发环境包括两个方面:
一是 linux 开发主机端的 tftp-server 支持,
二是嵌入式目标系统的 tftp-client 支持。
目标机安装
拨码开关:0111 —板子由FLASH启动
1000 —板子由SD启动
{//===============================开发环境搭建===================================
约定: “$”表示是在主机上执行,“#”表示在目标板执行
{//探讨
JACK: Hi,ivan, 嵌入式开发环境如何搭建啊,完全不知道如何下手啊?
IVAN:
网络搜索 /* 注: 如何搜索关键字,快速找到需要信息。是项目开发中重要的一种能力。 如下
“ubuntu 嵌入式 开发环境 搭建 ”
注意关键字要简明,且用空格隔开,方便搜索引擎查找
*/
JACK: TFTP服务不行啊,怎么办?
IVAN:
网络搜索 //“ubuntu tftp服务 安装”或 “嵌入式 tftp服务 安装”或“linux tftp服务 安装”
JACK: NFS服务不行啊,怎么办?
IVAN:
网络搜索 // “ubuntu NFS服务 安装”
}
{//开发主机安装
{//虚拟机安装(已有则跳过)
解压Ubuntu_12.04_64-bit_farsight.7z /*该镜像是在官方Ubuntu 12.04 LTS 64bit 基础上,安装了编译调试bootloader 内核,android 4.4源码所需的工具和库。
如交叉编译工具,VIM ,TFTP,NFS等
*/
点VMware-workstation-full-10.0.4-2249910.exe //按默认方式安装虚拟机
安装好后,选择“打开虚拟机”导入已安装好的镜像 Ubuntu_12.04_64-bit_farsight.vmx
点播放虚拟机,选我已移动该虚拟机
启动后,用户名是linux 密码是1
}
{//linux和window间共享
虚拟机 -> 设置 -> 选项 -> 共享文件夹 -> 添加 -> 选择需要共享的位置(如E盘)
$ ls /mnt/hgfs/E/ //有共享的内容显示,表示成功
$ cd ~
$ ln -s /mnt/hgfs/E/ e //创建软连接, 方便操作
}
{//交叉编译工具安装
$ tar xvf gcc-4.6.4.tar.xz
$ sudo vim /etc/bash.bashrc /* 添加交叉编译工具链的路径到系统脚本
在末尾添加
export PATH=/home/linux/store/gcc-4.6.4/bin:$PATH
注意路径要根据gcc-4.6.4的实际路径修改
*/
$ source /etc/bash.bashrc //使配置文件生效
$ arm-n 然后按Tab键补全 //如果能补全为arm-none-linux-gnueabi- 表示安装交叉编译工具成功了
}
{//u-boot编译(用已移植好的)
$ cp ~/e/fs4412/2系统移植/1实验/2第二天_U-boot移植/u-boot-2013.01-fs4412.tar.xz .
$ tar -xvf u-boot-2013.01-fs4412.tar.xz
$ cd u-boot-2013.01-fs4412
$ ./build.sh //运行该脚本后会自动生成最终的镜像 u-boot-fs4412.bin
$ cp u-boot-fs4412.bin ~/e/USB_fastboot_tool/platform-tools/ //通过后面的fastboot方式可写到板子上
}
{//内核编译(用已移植好的)
//配置编译关系 kconfig -> make menuconfig -> .config ->make -> makefile ->zImage
$ cp ~/e/fs4412/2系统移植/1实验/3第三天_内核移植/linux-3.14-fs4412.tar.xz .
$ tar -xvf linux-3.14-fs4412.tar.xz
$ cd linux-3.14-fs4412
$ make uImage //编译生成适合u-boot bootm启动的内核镜像 uImage
$ make dtbs /*编译设备树
make meunconfig //图像界面进行配置(如 指定新的驱动,内核裁剪)
make modules //只编译模块文件(.ko文件)
make //编译所有 (但不包括uImage生成)
*/
$ cp arch/arm/boot/uImage /tftpboot/
$ cp arch/arm/boot/dts/exynos4412-fs4412.dtb /tftpboot/
}
{//tftp服务安装
//----虚拟机上安装tftp服务
$ sudo dpkg -s tftpd-hpa //检查是否安装tftp server
$ sudo apt-get install tftpd-hpa //如果未安装,安装 tftp-server
$ sudo vi /etc/default/tftpd-hpa //修改tftp服务器配置文件为
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/tftpboot"
TFTP_ADDRESS="0.0.0.0:69"
TFTP_OPTIONS="-c -s -l"
$ mkdir /tftpboot //创建tftpboot目录,
$ sudo chmod a+w /tftpboot
$ sudo service tftpd-hpa restart //启动 tftp-server
//-----验证虚拟机tftp服务是否OK
$ sudo cat /etc/default/tftpd-hpa
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/tftpboot"
TFTP_ADDRESS="0.0.0.0:69"
TFTP_OPTIONS="-l -c -s"
$ sudo service tftpd-hpa restart //如果有更改配置,需要重启一下 tftp-server
$ cd /tftpboot
$ touch test
$ cd /tmp
$ tftp 127.0.0.1
tftp>get test
tftp>q
$ ls //看到有test文件,表示该tftp服务是OK的
//---准备待传输文件
拷贝 第一天_环境搭建里面里的 u-boot-fs4412.bin uImage exynos4412-fs4412.dtb 到 /tftpboot 目录下
}
{//nfs服务安装
$ service nfs-kernel-server /*检查nfs服务是否安装
如果显示下面信息, 表示有安装
* Usage: nfs-kernel-server {start|stop|status|reload|force-reload|restart}
如果没有,需安装
#sudo apt-get install nfs-kernel-server
*/
$ sudo vi /etc/exports /*修改配置文件,指定共享目录位置
在末尾追加
/nfs/rootfs *(rw,sync,no_root_squash)
或 /source/ *(rw,sync,no_subtree_check)
*/
$ sudo mkdir /nfs
$ sudo chmod 777 /nfs
$ cd /nfs
$ 拷贝 1第一天_环境搭建 下的 rootfs.tar.xz 到 /nfs 目录下 //rootfs.tar.xz是已制作好的根文件系统
$ tar -xvf rootfs.tar.xz
$ sudo chmod 777 rootfs
$ sudo /etc/init.d/nfs-kernel-server restart //重启nfs服务(使得前面修改生效)
$ sudo mount -t nfs localhost:/nfs/rootfs/ /mnt/ //测试nfs服务是否安装成功。
$ ls /mnt/ /*如果其中的内容和/nfs/rootfs中一致。 表示有挂载成功
如显示下面内容,表示成功
bin dev etc lib linuxrc mnt proc root sbin sys tmp usr var
*/
$ sudo umount /mnt
}
}
{//目标机安装(u-boot烧写调试) -- 需要SD卡
{//制作SD卡,并从SD卡启动 (使用的是2010版本u-boot,支持fastboot烧写的)
将sdfuse_q 拷贝到Linux下
将SD卡插入电脑并识别
进入sdfuse_q执行如下操作
$ sudo ./mkuboot.sh /dev/sdb /*出现下面的信息,表示SD启动盘制作成功
Fuse FS4412 trustzone uboot file into SD card
/dev/sdb reader is identified.
u-boot-fs4412.bin fusing...
1029+1 records in
1029+1 records out
527104 bytes (527 kB) copied, 5.31834 s, 99.1 kB/s
u-boot-fs4412.bin image has been fused successfully.
Eject SD card
*/
关闭开发板电源,将拨码开关SW1调至(1000)(SD启动模式)
刚才做好的SD启动盘插入SD卡插槽
打开电源
}
{//---fastboot烧写
连接USB线到板子上的USB OTG口
连接串口线到板子的COM2口
重启板子,并快速停下,在串口终端输入下面命令 /*
如果有有需要,可以格式化 eMMC 并创建分区
$ fdisk -c 0
$ fatformat mmc 0:1
$ ext3format mmc 0:2
$ ext3format mmc 0:3
$ ext3format mmc 0:4
*/
$ reset 重启切换到2010版的sd卡启动
$ fastboot //会提示装驱动,选中第一天_环境搭建里的fastboot_driver 安装
打开dos终端 进入USB_fastboot_tool\platform-tools目录输入
> fastboot.exe flash bootloader u-boot-fs4412.bin /*
同理可以烧录其它镜像
> fastboot.exe flash kernel zImage
> fastboot.exe flash ramdisk ramdisk-uboot.img
> fastboot.exe flash system system.img
> fastboot -w
*/
关闭开发板电源,将拨码开关SW1调至0110(EMMC启动模式)后打开电源
从flash 的u-boot启动 /*
如果启动失败,可以通过拨号开关恢复到SD卡方式启动
如果启动成功,想恢复到原来的u-boot启动 (第一次输入reset后会自动切换回去,不用拔拨号开关)
*/
}
}
{//1. 板子串口能显示信息
连接串口线到板子的COM2口 //如果是用笔记本电脑的,需装usb转串口驱动(用360驱动大师在线装方便)
确认拨号开关SW1 为0110 (从EMMC(FLASH的一种) 启动)
{//? 无串口打印信息
波特率设置不对, 流控未选为无
电脑用的可能是COM2
u-boot被破坏了,切换到SD卡启动(SW1 改为1000)
}
}
{//2. 板子能ping通虚拟机 (要关闭防火墙 网卡右键高级)
------------------------
|板子 192.168.9.9 |
------------------------
|
------------------------
|电脑 192.168.9.222 | //做中转用,和板子虚拟机IP要在同一网段, 且不能和它们IP一样
------------------------
|
------------------------
|虚拟机 192.168.9.120 |
------------------------
//---------确认板子和电脑是能通信的
设置电脑网卡ip 为192.168.9.222 255.255.255.0 192.168.9.1
启动板子,快速按任意键停在boot处,设置u-boot 的环境变量
# setenv serverip 192.168.9.120 //注意它与虚拟机里 ubuntu 的ip要一致
# setenv ipaddr 192.168.9.9
# setenv gatewayip 192.168.9.1
# pri /*查看设置后效果
FS4412 # pri
baudrate=115200
bootargs=root=/dev/nfs nfsroot=192.168.9.120:/nfs/rootfs rw console=ttySAC2,115200 clk_ignore_unused init=/linuxrc ip=192.168.9.9
bootcmd=tftp 41000000 uImage;tftp 42000000 exynos4412-fs4412.dtb;bootm 41000000 - 42000000
bootdelay=3
ethact=dm9000
ethaddr=11:22:33:44:55:66
gatewayip=192.168.9.1
ipaddr=192.168.9.9
netmask=255.255.255.0
serverip=192.168.9.120
*/
# ping 192.168.9.222 //测试网络是否连通 ,注意在u-boot中, 它能ping电脑, 电脑不能ping它
//注意,要拔掉jtag线,否则ping 会重启
host 192.168.9.222 is alive //is alive 表示ok
# saveenv 保存设置
//---------确认电脑和虚拟机时能通信
编辑 -> 虚拟网络编辑器 -> VMnet0 桥接到(选连板子的网卡) //注意不要用automic,应手动指定对应的网卡
虚拟机 -> 设置 -> 网卡适配器 -> 自定义 (选 VMnet0) /*使虚拟机内的虚拟网卡,关联到电脑实际用到的网卡
通过选VMnet0 和 VMnet1 。实现连板子,还是上internet的切换
*/
点ubuntu右上角网络图标 -> 右键选编辑连接 -> 全删掉 -> 添加(改新连接名称为board)
-> IPV4 设置 -> 方法(选手动) -> 点添加 设置为 192.168.9.120 255.255.255.0 192.168.9.1 并保存退出
点右上角网络图标,先断开,再选board连接 // 通过选board,和internet 实现连板子,还是上internet的切换
$ ifconfig //看ip是否已变为了192.168.9.120
$ ping 192.168.9.222 //测试电脑和虚拟机是否联通 (如不通,可尝试修复一下网卡,重启虚拟机)
//---------确认板子能和虚拟机能通讯
$ reset 重启boot
$ ping 192.168.9.120 //注意: boot阶段是板子能ping 电脑 ,电脑不能ping板子
host 192.168.9.120 is alive //通讯OK ,失败会显示not alive
}
{//3. 网络自动 tftp 加载内核,并挂载nfs rootfs 启动
# setenv bootcmd tftp 41000000 uImage\;tftp 42000000 exynos4412-fs4412.dtb\;bootm 41000000 - 42000000 /* 设置tftp方式加载内核
setenv bootcmd 设置环境变量(自启动命令 bootcmd)
tftp 41000000 uImage\; 通过tftp从虚拟机/tftpboot目录下下载内核uImage到板子的内存 41000000 处。 \; 用于分割多个命令
tftp 420000tft00 exynos4412-fs4412.dtb 通过tftp下载设备树文件 exynos4412-fs4412.dtb 到板子的内存 42000000 处
bootm 41000000 - 42000000 启动内核(41000000处放的是内核uImage, 42000000处放的是设备树文件)
*/
# setenv bootargs root=/dev/nfs nfsroot=192.168.9.120:/source/rootfs rw console=ttySAC2,115200 clk_ignore_unused init=/linuxrc ip=192.168.9.9 /* 挂载nfs rootfs
setenv bootargs root=/dev/nfs nfsroot=192.168.137.88:/nfs/rootfs rw console=ttySAC2,115200 clk_ignore_unused init=/linuxrc ip=192.168.137.66
setenv bootarg 设置环境变量( bootarg是启动参数)
root=/dev/nfs 指定根文件系统类型是 nfs
nfsroot=192.168.9.120:/source/rootfs 指定source rootfs的位置 (是在ip 是192.168.9.120的机器上 ,的/source/rootfs目录下). 注意/nfs/rootfs必须和前面NFS服务配置文件设置一致
*/
# savenenv //保存环境变量
掉电重启动板子 /* 看能否挂载nfs rootfs成功
能看到下面信息表示成功
[root@farsight ]# ls
etc linuxrc proc sbin tmp var
bin dev lib mnt root sys usr
在里面创建文件,电脑的/nfs/rootfs上同步变化
*/
}
}
(三四)、Bootloader移植
bootloader概念
- Bootloader是硬件启动的引导程序,是运行操作系统的前提;
- 在操作系统内核或用户应用程序运行之前运行的一小段代码。对软硬件进行相应的初始化和设定,为最终运行操作系统准备好环境;
- 在嵌入式系统中,整个系统的启动加载任务通常由Bootloader来完成。
bootloader特点
- Bootloader不属于操作系统,一般采用汇编语言和C语言开发。需要针对特定的硬件平台编写。
- 在移植系统时,首先为开发板移植Bootloader。
- Bootloader不但依赖于CPU的体系结构,而且依赖于嵌入式系统板级设备的配置。
bootloader操作模式
- 自启动模式:在这种模式下,Bootloader从目标机上的某个固态存储设备上将操作系统加载到RAM中运行,整个过程并没有用户的介入。
- 交互模式:在这种模式下,目标机上的Bootloader将通过串口或网络等通信手段从开发主机(Host)上下载内核映像和根文件系统映像等到RAM中。可以被 Bootloader写到目标机上的固态存储媒质中,或者直接进行系统的引导。也可以通过串口接收用户的命令。
u-boot介绍
u-boot(Universal Boot Loader)是德国DENX小组开发的用于多种嵌入式CPU的bootloader程序。遵循GPL条款。
从FADSROM、8xxROM 、PPCBOOT、Armboot逐步发展演化而来;
当前版本号:参考Makefile。
http://www.denx.de/wiki/U-Boot/WebHome
- U-boot的特点:
代码结构清晰、易于移植(见目录结构)
支持多种处理器体系结构(见arch目录)
支持众多开发板(目前官方包中有200多种,见board目录)
命令丰富、有监控功能
支持网络协议、USB、SD等多种协议和设备
支持文件系统
更新较活跃,使用者多,有助于解决问题
u-boot命令
命令分类
环境设置、数据传输、存储器访问、加载运行
-
printenv 显示所有环境变量
U-boot # printenv
baudrate=115200
ipaddr=192.168.1.100
ethaddr=12:34:56:78:9A:BC
serverip=192.168.1.10
…… -
setenv 设置新的环境变量
U-boot # setenv myboard FS4412
U-boot # printenv
baudrate=115200
ipaddr=192.168.1.100
ethaddr=11:22:33:44:55:66
serverip=192.168.1.10
myboard=FS4412
Environment size: 320/16380 bytes -
saveenv 将当前定义的所有的环境变量值存入flash中
-
tftp 通过网络下载程序
U-boot # setenv ethaddr 11:22:33:44:55:66
U-boot # setenv ipaddr 192.168.1.100
U-boot # setenv serverip 192.168.1.10
U-boot # tftp 41000000 application.bin
U-boot # tftp 41000000 zImage -
protect 对Nor Flash写保护
protect on 0 10000 对区间[0x0, 0x10000]进行写保护
protect off 0 10000 对上述区间取消写保护 -
erase 擦除Nor FLASH
erase all 擦除FLASH所有的扇区
erase 0 10000 把FLASH区间 [0x0, 0x10000]擦除 -
Nand相关命令
nand read addr off size
nand write addr off size
nand erase [clean] [off size] -
movi 命令
movi init —初始化eMMC并显示相关信息
movi read u-boot/kernel addr
movi write u-boot/kernel addr
movi read rootfs addr size
movi write rootfs addr size -
bootcmd 自启动命令
如果定义了该变量,在自启动模式下将会执行该环境变量中的命令。
U-boot # setenv bootcmd tftp 41000000 uImage; bootm 41000000
U-boot # saveenv -
go addr 执行内存中的二进制代码,简单的跳转到指定地址
bootm kernel-addr ramdisk-addr dtb-addr
引导内核为内核传参,其中内核和ramdisk通常为mkimage处理过的二进制文件。
u-boot配置编译
- U-boot的编译
整个工程通过Makefile来组织编译。顶层目录下的Makefile中包含了开发板的配置信息。从顶层目录开始递归地调用各级子目录下的Makefile,最后链接成u-boot映像。 - 顶层目录下的 Makefile
它负责u-boot整体配置和编译
在Makefile中指定使用的交叉工具链
配置u-boot: make origen_config
编译: make
U-BOOT编译生成的映像文件
U-boot镜像下载烧录
- 烧录编译产生的镜像 u-boot.bin
初次或开发板代码损坏不能正常启动时,可采用JTAG工具烧录 - 专用的烧录工具如h-jtag或DNW等
在u-boot已经能工作,升级或修正U-boot时,可用U-boot中的命令来烧录
其它方式 如SD卡 , Fastboot命令 - 镜像固化位置
ROM、NOR FLASH、NAND FLASH EMMC等
U-boot启动流程
uboot是板子上电后第一个程序,初始化一些硬件,做准备工作,接着引导加载内核
U-Boot 启动源码分析
- 第一条指令位置(参考u-boot.map) arch/arm/cpu/armv7/start.S 里的 _start: b reset
- 设置为SVC模式 msr cpsr,r0
- 关闭MMU Cache cpu_init_cp15
- 基本硬件设备初始化 board/samsung/fs4412/lowlevel_init.S 的 lowlevel_init
关中断 看门狗 ,初始化时钟 串口,flash,内存 - 自搬移到内存 relocate_code
- 设置栈, IRQ stack frame
=================================================== - 准备进入C部分 bl _main ( 参u-boot.map )
- 大部分硬件初始化 arch\arm\lib\board.c\board_init_f 里的init_sequence
- 搬移内核到内存运行 common/main.c main_loop -> getenv (“bootcmd”)
bootdelay >= 0 && s && !abortboot (bootdelay))
下的 run_command (bootcmd)
U-BOOT 移植方法
- 善用对比软件Beyond
- 选择官方源码版本下载, 配置编译
a. 指定交叉编译工具链
b. 指定cpu 和board(参考最类似配置如origen)
c. 编译 - 实现串口信息输出
a. 跟踪运行路径(led点灯法)
b. 串口输出(检查uart初始化相关部分代码 见lowlevel_init.s) - 网卡移植(实现能用tftp nfs 方便开发调试)
a. 寄存器地址
b. 参数设置 - FLASH移植(实现能下载软件到FLASH,产品能离线运行)
五、Linux内核
Linux内核基本概念
- 从技术上说 linux 是一个内核
- “内核”指的是一个提供硬件抽象层、磁盘及文件系统控制、多任务等功能的系统软件。一个内核不是一套完整的操作系统。
- 通常我们使用的 linux 系统是一个集 linux 内核、工具集、各种库、桌面管理器、应用程序等一体的一个发布包 (发行版)
Linux 发行版
Debian GNU/Linux
Red Hat Linux
Fedora Core
Ubuntu Linux
SUSE Linux
Gentoo Linux
Asianux
Slackware Linux
Turbo Linux
CentOS
Linux 内核的特性
- 免费开源
- 可以移植,支持的硬件平台广泛
arm, i386, m68k, m32r,m68knommu, mips, ppc, s390, sh, sparc - 高可扩展性
可剪裁、可扩展,可以运行在大型主机,也可以运行在个人计算机上 - 高可靠性、稳定性
稳定性是linux鲜明特点,安装了linux系统的主机,
连续运行一年不宕机是很平常的事情 - 超强的网络功能
- 真正的多任务,多用户系统
- 模块化设计
模块可以动态加载,卸载,可以减少系统体积,同时可以用来解决冲突问题,模块调试
linux 内核版本
- 目前linux系统采用 A.B.C.D 的版本号管理方式
A 主版本号
B 次版本号 为偶数表示 稳定版本 奇数为开发中版本
C 表示linux的发行版本号
D 表示更新版本号 - 主版本(X.Y)
1.0 2.0 2.2 2.4 2.6 3.x
Linux内核模块结构图
linux 不会是一个真正的实时系统 (时间片轮转方式)
vxwork 是一个真正的实时(RTOS )系统 收费
linux 分层
应用程序
||
\/
系统调用
||
\/
linux 内核 分为 平台无关吗 和 平台相关码
||
\/
硬件平台
编译内核(已移植好的)
linux 内核 下载地址 https://www.kernel.org/
linux 各个版本下载地址 https://www.kernel.org/pub/linux/kernel/
- 编译内核 make uImage
- 编译设备树 make dtbs
上面两个命令都需要在 源码的顶层目录上 使用 ☆☆☆
嵌入式系统启动信息分析
- u-boot启动阶段
U-Boot 2013.01 (Aug 24 2014 - 12:01:19) for FS4412
CPU: Exynos4412@1000MHz
Board: FS4412
DRAM: 1 GiB
……Loading: *######################
Starting kernel … - linux内核启动阶段
Booting Linux on physical CPU 0xa00
Linux version 3.14.0 (david@ubuntu)
CPU: ARMv7 Processor [413fc090] revision 0 (ARMv7), cr=10c5387d
Machine model: Insignal Origen evaluation board based on Exynos4412
IP-Config: Complete:
VFS: Mounted root (nfs filesystem) on device 0:10 - 根文件系统阶段(可运行应用程序)
[root@farsight ]# ls
a.out dev lib mnt root sys usr
bin etc linuxrc proc sbin tmp va
嵌入式系统 启动流程
- bootloader启动步骤
//阶段一(汇编)
设置为SVC模式,关闭中断,MMU,看门狗
基本硬件设备初始化 //初始化时钟,串口,flash,内存
自搬移到内存
设置好栈 跳转到C阶段
//阶段二(C语言)
大部分硬件初始化
搬移内核到 内存
运行内核内核启动流程
a. 自解压内核 decompess (arch/arm/boot/compressed/head.S)
b. 运行内核汇编部分 head.S 入口stext (arch/arm/kernel/head.S)
检测合法性(CPU 类型,机器类型)
c. 运行内核C部分 start_kernel (init/main.c)
CPU,机器参数的安装 setup_arch
中断,定时,终端,内存等最基本的初始化
创建核心进程 kernel_init运行,启动多任务调度
d. 挂载rootfs
e. 运行第一个应用程序init (一般是 linuxrc)
Linux内核调试方法
-
内核调试方法 点灯法
ldr r0, =0x11000c40 @GPK2_7 led2
ldr r1, [r0]
bic r1, r1, #0xf0000000
orr r1, r1, #0x10000000
str r1, [r0]
ldr r0, =0x11000c44
mov r1,#0xff
str r1, [r0] -
printk打印输出信息
puts (内核解压前)
printascii (console初始化前)
printk (内核解压后,信息输出显示是在 console 初始化之后)通过proc在运行时查看和修改日志级别
cat /proc/sys/kernel/printk 显示 4 4 1 7
echo “7 4 1 7” > /proc/sys/kernel/printk 后
cat /proc/sys/kernel/printk 显示7 4 1 7 -
printk打印输出信息
打印级别:
#define KERN_EMERG “<0>” /* system is unusable /
#define KERN_ALERT "<1>” / action must be taken immediately /
#define KERN_CRIT “<2>” / critical conditions /
#define KERN_ERR “<3>” / error conditions /
#define KERN_WARNING “<4>” / warning conditions /
#define KERN_NOTICE “<5>” / normal but significant condition /
#define KERN_INFO “<6>” / informational /
#define KERN_DEBUG “<7>” / debug-level messages
printk( KERN_INFO “ \n INFO Level \n”);
- OOP内核异常信息
-
制造错误
修改drivers/char/fs4412_led_drv.c
在s5pv210_led_init函数中int ret=0;下增加下面语句: int *ptr = NULL; *ptr = 0xff; -
运行该内核报错
[ 1.165000] Unable to handle kernel NULL pointer dereference at virtual address 00000000
[ 1.170000] pgd = c0004000
[ 1.175000] [00000000] *pgd=00000000
[ 1.175000] Internal error: Oops: 805 [#1] PREEMPT SMP ARM
[ 1.180000] Modules linked in:
[ 1.185000] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.14.0 #25
[ 1.190000] task: ee8a0000 ti: ee8a4000 task.ti: ee8a4000
[ 1.195000] PC is at s5pv210_led_init+0x18/0x180
[ 1.200000] LR is at do_one_initcall+0x30/0x144
[ 1.205000] pc : [] lr : [] psr: 60000153
[ 1.205000] sp : ee8a5ef8 ip : c059afac fp : 00000000
[ 1.215000] r10: c052d4fc r9 : c0564b80 r8 : c0242244
[ 1.220000] r7 : c05a3400 r6 : c055134c r5 : 00000000 r4 : ee8a4000
[ 1.230000] r3 : 00000055 r2 : c04c0430 r1 : 00000001 r0 : 1f400000
[ 1.235000] Flags: nZCv IRQs on FIQs off Mode SVC_32 ISA ARM Segment kernel
[ 1.245000] Control: 10c5387d Table: 4000404a DAC: 00000015
[ 1.250000] Process swapper/0 (pid: 1, stack limit = 0xee8a4240)
[ 1.255000] Stack: (0xee8a5ef8 to 0xee8a6000) -
找出错位置
根据PC is at s5pv210_led_init+0x18/0x180 知道出错的函数是s5pv210_led_init
根据pc : [] 知道出错的位置
#arm-none-linux-gnueabi-addr2line c024225c -e vmlinux -f 在源码中会显示具体出错的位置
六、Linux内核移植与网卡移植
Linux内核移植
Linux内核配置编译
-
下载内核源码 (谷歌搜索 linux-3.14.tar.xz ,会很快找到有许多内核版本的列表)
-
Linux系统中解压 ( tar -xvf linux-3.14.tar.xz 注意不能在与window的共享目录解压)
-
修改Makefile指定交叉编译工具链
-
导入配置 make exynos_defconfig(配置列表见 arch/arm/configs/ 找最类似的)
-
配置内核 make menuconfig
-
编译内核 make uImage
-
编译设备树 make dtbs
网卡移植
网卡移植 平台无关
- 配置内核支持网络
$ make menuconfig - 配置网络协议支持TCP/IP
[] Networking support —> //注意要先输入y 选择该菜单,再按enter键,才能看到下面的选项
Networking options —>
<> Packet socket
<> Unix domain sockets
[] TCP/IP networking
[*] IP: kernel level autoconfiguration - 配置支持网络文件系统 NFS
File systems —> [] Network File Systems —>
<> NFS client support
<> NFS client support for NFS version 2
[] NFS client support for NFS version 3
[] NFS client support for the NFSv3
ACL protocol extension
[] Root file system on NFS - 配置支持dm9000网卡驱动
Device Drivers —>
[] Network device support —>
[] Ethernet driver support —>
<*> DM9000 support
网卡移植 平台相关
- 配置设备树描述网卡和CPU的链接情况
$ vim arch/arm/boot/dts/exynos4412-fs4412.dts 在 regulators 前添加下面代码
srom-cs1@5000000 {
compatible = “simple-bus”;
#address-cells = <1>;
#size-cells = <1>;
reg = <0x5000000 0x1000000>; 对应芯片手册 3 Memory Map 的0x0500_0000 和 16 MB ranges;
ethernet@5000000 {
compatible = “davicom,dm9000”; 内核通过该名字来匹配驱动
reg = <0x5000000 0x2 0x5000004 0x2>; 寄存器地址和数据宽度
interrupt-parent = <&gpx0>; 继承于 中断控制器gpx0
interrupts = <6 4>; 6 对应中断源 DM9000_IRQ -> XEINT6 。4对应 active high level-sensitive davicom,no-eeprom;
mac-address = [00 0a 2d a6 55 a2];
};
}; - 修改文件driver/clk/clk.c static bool clk_ignore_unused;改为static bool clk_ignore_unused = true;
CPU与设备连接描述 - 设备树DeviceTree
-
Device Tree是描述硬件信息的数据结构
用于管理 硬件拓扑和硬件资源信息。
Device Tree由一系列被命名的结点(node)和属性(property)组成,而结点本身可包含子结点。
所谓属性,其实就是成对出现的name和value。 -
帮助
百度:linux Device Tree 详解
官网:http://www.devicetree.org 和 http://elinux.org/Device_Tree
源码实例:
说明: Documentation/devicetree/bindings/arm
源码: arch/arm/boot/dts/exynos4412-origen.dts
在内核里有一个结构“struct machine_desc”,内核用这个结构表示一个实际存在的板子,而针对每个板子都会有一个文件定义这个结构体,这个文件叫平台代码;
如:arch/arm/mach-s5pv21/mach-smdkv210.c(新版本内核中没有基于Exynos4412的平台代码,这里以s5pv210为例)
MACHINE_START(SMDKV210, “SMDKV210”)
/* Maintainer: Kukjin Kim kgene.kim@samsung.com */
.atag_offset = 0x100,
.init_irq = s5pv210_init_irq,
.map_io = smdkv210_map_io,
.init_machine = smdkv210_machine_init,
.init_time = samsung_timer_init,
.restart = s5pv210_restart,
.reserve = &smdkv210_reserve,
MACHINE_END
七、第三方驱动移植
1.第三方驱动 黑盒移植
-
编译驱动进内核
a. 选择驱动存放目录 (或任意目录)
b. 改Makefile
c. 改Kconfig (界面可配置) -
编译驱动为独立的模块
a. 配置为模块方式
b. make modules 编译为模块
c. 创建设备节点(应用访问驱动的入口)
d. 运行测试驱动的应用程序
2.第三方驱动 白盒移植
- 打印跟踪
- 驱动框架
八、根文件系统制作
1.根文件系统概念
- 根文件系统(root filesystem)是存放运行、维护系统所必须的各种工具软件、库文件、脚本、配置文件和其他特殊文件的地方,也可以安装各种软件包。
- 程序文件目录
/bin: 普通用户和root用户都能执行的基本程序
ping, mknod, mount, tar, grep, gzip, etc
/sbin: root用户能执行的基本程序
int, insmod, route, mkfs, rmmod, ifconfig
/usr/bin: 更多非必须的用户程序
autorun, bibtex, latex, biff, ftp, wc, whereis, whoami
/usr/sbin: 更多非必须的root工具程序
automount, httpd, in.telnetd, in.talkd, sendmail - 配置基本的linux命令(嵌入式linux通过busybox制作)
cat, chmod, chown, cp, chroot, copi, date, dd, df, dmesg, dos2unix, du, echo, env, expr, find, grep, gunzip, gzip, halt, id, ifconfig, init, insmod等等 - 配置用户自己的应用
桌面管理器等等 - 库文件的放置
/lib: 系统和运行基本命令时需要的动态库文件
/usr/lib:所有的其他库
/usr/lib/xxx: 一些工具包的私有库
如:/usr/lib/perl5 - linux设备文件
Linux 系统中所有的对象(包括设备)都是以文件的形式体现的
Linux系统中,所有的设备文件(如:设备节点),通常放到 /dev下
嵌入式系统中只需要创建必须的设备节点即可 - 设备的主设备号次设备号
Linux系统是通过主设备号和次设备号来区分设备的
主设备号: (major)
内核用来区分哪类设备
次设备号: (minor)
区分某类设备中的哪个设备
内核中的相关文档 Documentation/devices.txt - 创建设备节点
设备文件不能在加载驱动程序时自动创建,要通过指令创建
创建设备文件的一般语法:
$ mknod /dev/ [c|b]
例如:
$ mknod /dev/ttySAC0 c 4 64
$ mknod /dev/hda1 b 3 1
2.Linux系统的引导过程
3.BusyBox项目构建系统命令与文件系统制作
(1)BusyBox项目构建系统命令
- BusyBox 项目是由Bruce Perens in 在1996创建的
http://www.busybox.net/
BusyBox 是在 GNU GPL 许可协议下发行的开源软件 - 享有“嵌入式Linux的瑞士军刀”美誉,Erik Andersen先生维护;
- Busybox是一个UNIX系统工具集,它将很多普通的UNIX工具集成到一个很小的可执行文件中,为普通用户提供大多数常用的命令;
- BusyBox常用于制作linux命令 主要指令包括
cat, chmod, chown, cp, chroot, copi, date, dd, df, dmesg, dos2unix, du, echo, env, expr, find, grep, gunzip, gzip, halt, id, ifconfig, init, insmod, etc
(2)制作根文件系统的内容
- 制作根文件系统的内容
采用Busybox创建基本命令
创建基本的目录 /lib /etc /var /tmp /dev /sys /proc等
添加glibc基本动态库
创建基本的设备节点
添加启动配置和脚本程序 /etc/inittab /etc/fstab /etc/init.d/rcS - 测试rootfs内容正确性
- 制作需要的rootfs类型的格式
制作根文件系统的内容
$ tar xvf busybox-1.22.1.tar.bz2
$ cd busybox-1.22.1
$ make menuconfig
Busybox Settings --->
Build Options --->
[*] Build BusyBox as a static binary (no shared libs)
(arm-none-linux-gnueabi-) Cross Compiler prefix 注意 一定要指定交叉编译工具
$ make
$ file busybox 确认编译生成的是 ARM 平台的(显示为ELF 32-bit LSB executable, ARM)
$ make install 安装(默认安装路径为_install)
$ cd _install
$ ls
bin linuxrc sbin usr
$ mkdir dev etc mnt proc var tmp sys root 创建需要的目录
$ cp ~/store/gcc-4.6.4/arm-arm1176jzfssf-linux-gnueabi/lib/ . -a 注意是lib/
$ du -mh lib 查看lib库的大小
$ rm lib/*.a 裁剪,删除掉静态库文件
$ arm-none-linux-gnueabi-strip lib 裁剪掉调试信息 not recognized 有些库是不能strip的 忽略掉
$ sudo rm lib/libstdc++* 删除不需要的库,确保所有库大小不超过4M
$ du -mh lib 查看lib库的大小 可能 显示3.4M lib (这里确保小于 8M)
$ cp /nfs/rootfs/etc -rf . 拷入成熟的参考配置
$sudo mknod dev/console c 5 1 必须要有 console设备节点
(3)测试rootfs内容正确性
$ cd /nfs
$ mkdir rootfs
$ cp _install/* rootfs –a
$ chmod 777 /nfs/rootfs/
NFS能挂载成功表示根文件系统 内容基本正确