系统移植
一、环境搭建/系统部署
【1】安装交叉编译工具链
1>为什么要安装交叉编译工具链
2>如何安装和配置交叉编译工具链
注意:1.在实际的开发中,不同的硬件平台,使用的交叉编译工具链的版本可能不一致
2.Ubuntu源码或linux内核源码的版本不一致,可能交叉编译工具链的版本也不一致
【2】安装tftp服务(Trivial File Transfer Protocol)
1>概念
tftp服务器是基于TCP/IP的简单文件传输协议;
基于TCP/IP协议的一个用来在客户机与服务器之间进行简单文件传输的协议
2>作用
基于网络,使用tftp服务器下载程序到开发板
3>安装tftp服务器具体步骤
①检查Ubuntu是否安装了tftp服务
sudo dpkg -s tftpd-hpa
打印以下内容表示安装了tftp服务器
Architecture: i386
Source: tftp-hpa
Version: 5.2-7ubuntu3.1
②安装tftp服务器
sudo apt-get install tftpd-hpa tftp-hpa
解析命令:
tftpd-hpa:tftp服务器端
tftp-hpa:tftp客户端
如果安装失败,可以更新源和依赖:
sudo apt-get update --->更新源
sudo pt-get install -f ---->更新依赖
③配置tftp服务器
1.在家目录下创建一个tftpboot文件夹
cd /home/linux
mkdir tftpboot
目的:tftpboot目录下存放的是你要下载到开发板上的可执行文件
2.修改tftpboot文件夹的权限
chmod 777 tftpboot
3.配置tftp服务器的环境变量
打开:sudo vi /etc/default/tftpd-hpa
修改一下内容:
TFTP_USERNAME="tftp" # tftp用户名,不需要修改
TFTP_DIRECTORY="/home/linux/tftpboot"
# tftp服务下载文件的存放的路径,需要修改
# 改成自己的对应的tftpboot的路径
TFTP_ADDRESS="0.0.0.0:69"
# tftp服务默认使用的69端口号
TFTP_OPTIONS="-c -s -l"
解析:tftp服务的参数,这个需要修改
-l:以standalone/listen模式启动TFTP服务
-c:可创建新文件,默认情况下,tftp只允许覆盖原有文件,不能创建新文件
-s:改变tftp启动的根目录
加了-s后,客户端使用tftp时,不需要输入指定目录,填写文件的完整路径,而是使用配置文件中写好的目录,这样也可以增加安全性
④重启tftp服务器
sudo service tftpd-hpa restart
⑤本地测试tftp服务是否安装成功
⑥上传和下载不成功原因
关闭windows和Ubuntu的防火墙(Ubuntu的防火墙默认是关闭的)
sudo ufw disable
【3】安装nfs服务(Network File System)
1>作用
让开发板通过网络的方式远程从服务器端挂载根文件系统
2>安装nfs服务器的步骤
①检查nfs服务是否安装
sudo dpkg -s nfs-kernel-server
②安装nfs服务
sudo apt-get install nfs-kernel-server
③配置nfs服务
1》在家目录下创建nfs文件夹
mkdir nfs
2》设置文件夹的权限最大
chmod 777 nfs
3》拷贝根文件系统到nfs目录下
cp /mnt/hgfs/share/rootfs-ok.tar.bz2 ~/nfs
4》对根文件系统的压缩包进行解压缩
cd ~/nfs
tar -vxf rootfs-ok.tar.bz2
5》配置nfs服务的环境变量
打开sudo vi /etc/exports
在文件的最后一行添加以下内容:
/home/linux/nfs/rootfs/ *(rw,sync,no_subtree_check,no_root_squash)
解析命令:
/home/linux/nfs/rootfs/:自己的根文件系统的路径
*:所有的用户
rw:可读可写的权限
sync:同步文件
no_subtree_check:不对子目录检查文件的权限
no_root_squash:如果客户端为root用户,那么他对整个文件具有root的权限
④重启nfs服务器
sudo service nfs-kernel-server restart 重启nfs服务
【4】gnu组织命令
1>gcc:编译
arm-none-linux-gnueabi-gcc ***.c -o ***.o
2>ld:将 .o文件链接生成.elf文件
arm-none-linux-gnueabi-ld -T map.lds ****.o -o ***.elf
3>objcopy:生成二进制文件
arm-none-linux-gnueabi-objcopy -O binary ***.elf ***.bin
4>objdump:生成反汇编文件
arm-none-linux-gnueabi-objdump -D ***.elf > ***.dis
5>nm:查看elf文件的符号表
arm-none-linux-gnueabi-nm ***.elf
6>readelf:读取elf文件的头信息
arm-none-linux-gnueabi-readelf -h ***.elf
7>size:查看elf文件各个段的大小
arm-none-linux-gnueabi-size ***.elf
8>strip:压缩文件体积,去除一些无用符号信息,不影响程序的运行
arm-none-linux-gnueabi-strip ***.elf注意:不可以对中间文件执行strip命令,比如***.o文件
9>addr2line:根据地址信息定为错误位置,常用于段错误
arm-none-linux-gnueabi-addr2line 0x43c01724 -e interface.elf -f
0x43c01724:出现段错误的地址,这是在反汇编文件中拿到的一条指令的地址
-e:后边跟elf文件
-f:回显函数名及函数所在的文件,及行号
【1】bootloader和u-boot的关系
boot:引导
loader:加载
bootloader是一系列引导 加载程序的统称
u-boot,Bios,Vivi属于bootloader中的一种
嵌入式开发中使用频率最高的引导程序就是u-boot
【5】u-boot的特点
u-boot源码由德国DENX小组进行维护
1)u-boot是一个开源的软件
2)u-boot支持多种架构的硬件平台(arm、X86、mips、powerPC、RISC-V)
3)u-boot源码短小精悍
4)u-boot是一个短命鬼,因为u-boot的主要作用是引导加载linux内核或者应用程序启动,一旦内核或应用程序启动,u-boot的使用就结束了
5)u-boot源码本质就是一个裸机程序
6)u-boot启动时,主要完成部分硬件的初始化,比如:内存,EMMC,时钟,串口,网卡,屏幕等
7)u-boot主要用于引导linux内核启动,并给内核传递必要的启动参数
【6】u-boot支持的命令
1、u-boot有两种工作模式
①交互模式:倒计时减到0之前,按下任意键,进入交互模式,此时可以输入u-boot的命令
②自启动模式:倒计时减到0之前,不按下任意键,进入到自启动模式
2、help命令:打印u-boot支持的所有命令 help u-boot
3、loadb:下载二进制文件到内存中
4、go:运行二进制程序
5、printenv/print/pri:打印u-boot的环境变量
baudrate=115200 波特率
bootcmd = 自启动命令(?)
bootargs = 自启动参数(?)
bootdelay=3 倒计时时间
gatewayip=192.168.43.1 开发板网关
ipaddr=192.168.43.3 开发板IP地址
netmask=255.255.255.0 开发板子网掩码
serverip=192.168.43.7 服务器(PC)IP地址
stderr=serial 标准错误
stdin=serial 标准输入
stdout=serial 标准输出
注意:u-boot在对u-boot命令匹配时是部分匹配的
6、对u-boot中的环境变量进行增、删、改
setenv:设置环境变量,设置的结果保存在内存中,掉电丢失
saveenv:保存环境变量,将内存中新设置的环境变量,保存到flash中
增加格式: setenv 新的name value
删除格式:setenv 要删除的name
修改格式:setenv 要修改的name value
7、ping命令:检查开发板是否可以网络连通
8、tftpboot/tftp命令:使用tftp服务器下载,从Ubuntu服务器下载程序到开发板的内存中
下载格式:tftpboot 地址 文件
9、reset命令:复位开发板中的u-boot
10、boot:理解执行bootcmd环境变量中的u-boot命令
【7】测试ping命令和tftp命令的使用
1、准备工作
①Ubuntu需要安装tftp服务器
②关闭Ubuntu和Windows的防火墙
③修改电脑的有线网卡(USB转网卡)为百兆全双工
2、开发板和PC端进行连接
3、配置Ubuntu的网络
①配置为桥接模式,选择跟开发板进行连接的有线网卡
②配置Ubuntu的IP地址为静态的,不要自动获取,这样保证开发板和Ubuntu服务器在同一个网段
4、配置开发板的网络
配置u-boot中一下几个环境变量
serverip:Ubuntu服务器IP地址
ipaddr:开发板IP地址
netmask:子网掩码
gatewayip:网格
5、使用ping命令,测试开发板是否可以ping同Ubuntu
6、使用tftp命令下载interface.bin到开发板的内存中
【8】开发阶段系统的部署方式
1、开发阶段部署方式
u-bootpak.bin:u-boot源码编译生成
uImage:linux内核源码编译生成
rootfs:busybox编译生成
u-bootpak.bin:flash/SD卡
uImage:使用tftp下载到内存中
rootfs:使用nfs从服务器挂载
2、部署步骤
①拷贝uImage到Ubuntu的~/tftpboot目录下
②启动开发板,进入交互模式
③使用tftpboot命令下载uImage到开发板的内存中
④设置u-boot给内核启动时的传递的参数(bootargs:自启动参数)
u-boot把bootargs中参数传递给linux内核,告诉linux内核从哪里挂载根文件系统,是通过nfs网络挂载,还是从RAM中挂载
setenv bootargs root=/dev/nfs nfsroot=192.168.1.250:/home/linux/nfs/rootfs tcp,v4 rw console=/dev/ttySAC0,115200 init=/linuxrc ip=192.168.1.222
解析命令:1.root=/dev/nfs:使用nfs服务挂载根文件系统2.nfsroot=192.168.1.250:/home/linux/nfs/rootfs 指定挂载根文件系统的服务器的IP地址和路径
3.tcp,v4 --》 nfs服务的版本号
4.rw --》对根文件系统具有可读可写的权限
5.console=/dev/ttySAC0,115200 --》 使用串口0,波特率为115200
6.init=/linuxrc --> 系统启动成功,运行的1号进程为linuxrc
7.ip=192.168.1.222 --》 指定开发板的IP地址
5、启动linux内核,并挂载根文件系统
6、编写hello world应用程序,进行测试
7、设置开发板为自启动模式
设置u-boot中的bootcmd命令(自启动命令)
倒计时减到0之前,不按下任意键,进入自启动模式,此时会自动的执行bootcmd后边u-boot命令
8、18.04以上版本需要配置文件
https://www.jianshu.com/p/10e3245f15f3?tdsourcetag=s_pctim_aiomsg
【9】烧写ubootpak.bin到flash/SD卡中
1)使用windows工具将ubootpak.bin烧写到SD卡中(参考文档)
2)使用Ubuntu中的工具sdtool烧写到SD卡中
注:需要使用读卡器,不要使用电脑自带的SD卡
1>拷贝sdtoo工具到Ubuntu中
2>将sd卡被Ubuntu识别;
先对SD卡进行格式化(在Windows下格式化);
需要修改vmvare兼容3.0
虚拟机->设置->usb控制器->usb兼容性->usb3.0
设置Ubuntu识别SD卡:
虚拟机->可移动设备->读卡器名称->连接
3>烧写ubootpak.bin到SD卡中
①进入sdtool目录下
②执行烧写ubootpak.bin的命令
sudo ./s5p6818-sdmmc.sh /dev/sdb ubootpak.bin解析:./s5p6818-sdmmc.sh:烧写的脚本文件ubootpak.bin:u-boot镜像文件/dev/sdb:SD卡对应的块设备文件
4>以SD卡的方式重新启动开发板
5>分析s5p6818-sdmmc.sh脚本文件
dd if="${xboot}" of="${dev}" bs=512 seek=1 conv=sync解析命令:dd:拷贝命令,将某个文件写到一个设备文件中if:输入文件of:输出文件bs:一块的大小是512字节内存是按字节访问,FLASH按块进行访问,一块是512字节seek:偏移一块空间的大小,跳过512字节conv=sync :数据同步SD卡的最前边的512字节属于SD卡的分区表
3)使用u-boot提供的update_mmc命令将ubootpak.bin烧写到开发板的EMMC(Flash)中
前提:必须制作号SD卡的启动盘
1.使用SD卡的方式启动开发板的uboot,进入uboot的交互界面2.使用tftp方式下载ubootpak.bin到内存中3.使用update_mmc命令将诶村中的ubootpak.bin搬移到EMMC中4.切换到EMMC的方式启动uboot
1》切换开发为sd卡的启动方式,进入uboot的交互界面
2》拷贝ubootpak.bin文件到Ubuntu的~/tftpboot文件夹中
3》使用tftp命令下载ubootpak.bin到内存中
tftp 0x41000000 ubootpak.bin
4》使用update_mmc命令搬移uboot到EMMC中
update_mmc 2 2ndboot 0x41000000 0x200 0x7800
解析命令:
update_mmc <dev no> <type> <mem> <addr> <length>
<dev no> :EMMC设备编号
<type> :类型
<mem> :内存的起始地址
<addr> :EMMC的起始地址
<length>:烧写的数据长度
5》切换到EMMC的方式启动,观察现象是否启动成功
【10】产品阶段系统的部署方式
u-bootpak.bin:u-boot源码编译生成
uImage:linux内核源码编译生成
rootfs:busybox编译生成
ramdisk.img:rootfs打包压缩生成
u-bootpak.bin:flash/SD卡
uImage:Flash
rootfs:Flash
1、mmc命令
1)mmc read
mmc read addr blk# cntmmc read :将EMMC中的内容读到内存中addr :内存的起始地址blk# :EMMC的起始块号cnt:读数据的总块数
2)mmc write
mmc write addr blk# cntmmc write :将内存中的数据写到EMMC中addr :内存的起始地址blk# :EMMC的起始块号cnt :写数据的总块数
3)mmc erase
mmc erase blk# cntmmc erase :擦除EMMC中的数据blk# :擦除的起始块号cnt :擦除的块数
1.开发板上电,启动u-boot,进入u-boot的交互界面
2.使用tftp命令下载uImage到内存中
3.使用mmc write将内存中的uImage搬移到EMMC中
4.使用tftp命令下载ramdisk.img到内存中
5.使用mmc write将内存中的ramdisk.img搬移到EMMC中
2、流程
1)启动开发板,进入u-boot的交互模式
2)将uImage和ramdisk.img拷贝到~/tftpboot文件夹
3)使用tftp下载uImage文件到内存中
tftp 0x41000000 uImage
4)使用mmc write将内存中的uImage写到EMMC中
mmc write 0x41000000 0x800 0x4000
5)使用tftp下载ramdisk.img到内存中
tftp 0x41000000 ramdisk.img
6)使用mmc write将内存中的ramdisk.img写到EMMC中
mmc write 0x41000000 0x20800 0x20800
7)设置bootargs的自启动参数,告诉内核从ram中挂载根文件系统
setenv bootargs root=/dev/ram rw init=/linuxrc console=/dev/ttySAC0,115200initrd=0x49000040,0x1000000 rootfstype=ext4saveenv
解析: root=/dev/ram :从ram设备中挂载根文件系统
rw :可读可写权限
init=/linuxrc :1号进程
console=/dev/ttySAC0,115200 :串口0,115200
initrd=0x49000040,0x1000000 :0x49000000:根文件系统在内存中的地址0x1000000 :根文件系统的大小0x49000040:跳过根文件系统前的64字节的头部信息
rootfstype=ext4 :根文件系统的格式ext4
8)使用mmc read将EMMC中的uImage读到内存中
mmc read 0x48000000 0x800 0x4000
9)使用mmc read将EMMC中的ramdisk.img读到内存中
mmc read 0x49000000 0x20800 0x20800
10)启动linux内核
bootm 0x48000000 - 0x49000000
二、u-boot源码的移植
【1】uboot的源码的获取
【2】uboot源码的命名方式
【3】移植的准备工作
1、了解硬件平台的相关信息
cpu:Cortex-A53 * 8
Arch:ARM
vendor:sumsung
SOC:s5p6818
公板:FS6818
【4】移植u-boot
1、准备u-boot源码
1> 拷贝u-boot源码的压缩包到ubuntu中
2> 对u-boot源码进行解压缩 tar -vxf u-boot-2014.07-netok.tar.bz2
3> 进入到u-boot源码目录下 cd u-boot-2014.07
2、分析u-boot源码的目录结构
平台相关:跟硬件有关 arch : 架构相关的代码 board: 板子相关的代码
平台无关:跟硬件无关 drivers : 驱动相关 fs : 文件系统 common :
uboot相关命令 lib : 库 tools : 工具 ......
3、对u-boot源码进行配置和编译
配置:配置源码支持自己的硬件平台
编译:只编译跟自己硬件平台相关的代码,生成ubootpak.bin
4、对u-boot源码进行配置
1)make help 获取make相关的帮助
清除目标文件:
clean
mrproper
distclean
编译u-boot源码
all
make V=0 [targets] :默认编译,不回显详细的编译信息
make V=1 [targets] :回显详细的编译信息
Execute "make" or "make all" to build all targets marked with [*]
For further info see the ./README file
打开README,分析README文件,得到配置板子的命令
For all supported boards there are ready-to-use default
configurations available; just type "make <board_name>_config".
Example: For a TQM823L module type:
cd u-boot
make TQM823L_config
5、对u-boot源码进行配置和编译
1)修改u-boot源码顶层目录下的Makefile,配置交叉编译工具链
ifeq ($(ARCH),$(HOSTARCH))
CROSS_COMPILE ?=
endif
修改为:
ifeq (arm,arm)
CROSS_COMPILE ?= arm-none-linux-gnueabi-
endif
2)清除源码删词编译生成的中间文件
make distclean
3)配置 u-boot源码支持fs6818硬件平台
make fs6818_config
4)编译u-boot源码
time make -j4 all
5)编译成功之后,得到ubootpak.bin,测试ubootpak.bin
6)ubootpak.bin输入Ubuntu版本的uboot镜像文件,需要得到windows版本的镜像文件,才能使用windows工具
**使用Ubuntu方式烧写,已经跳过512字节的分区表
**使用windows工具烧写默认从0开始烧写,因此需要跳过分区表
①制作512字节的文件
dd if=/dev/zero of=./512B bs=512 count=1 conv=sync
②将512文件权限改成777
③得到win_ubootpak.bin
cat 512B ubootpak.bin > win_ubootpak.bin
【5】给uboot源码移植loadb命令
1、u-boot源码是否存在loadb命令的源码
grep "loadb" * -nR
得到以下信息:
common/cmd_load.c:389: * loadb command (load binary) included
common/cmd_load.c:1059: loadb, 3, 0, do_load_serial_bin,
2、打开common/cmd_load.c文件定为到389,1059行
得到以下信息,说明已经有了loadb命令的源码
1057 #if defined(CONFIG_CMD_LOADB)
1058 U_BOOT_CMD(
1059 loadb, 3, 0, do_load_serial_bin,
1060 "load binary file over serial line (kermit mode)",
1061 "[ off ] [ baud ]\n"
1062 " - load binary file over serial line"
1063 " with offset 'off' and baudrate 'baud'"
1064 );
........
1081
1082 #endif /* CONFIG_CMD_LOADB */
3、思考:有可能cmd_load.c文件没有被编译
133 obj-y += cmd_load.o
说明cmd_load.c文件被编译。
原理:
all:u-boot.bin
u-bootpak.bin
u-boot.bin:u-boot
objcopy
u-boot:$(obj-y)
ld ***.o
%.o:%.c
gcc ***.c -o ***.o
4、思考:有可能CONFIG_CMD_LOADB宏没有定义
查找板子的配置信息的头文件:find . -name fs6818.h 打开 vi ./include/configs/fs6818.h添加一下信息:121 #define CONFIG_CMD_LOADB
【6】分析make fs6818_config执行过程详解
1、打开uboot源码顶层目录的Makefile,搜索_config
467 %_config:: outputmakefile
468 @$(MKCONFIG) -A $(@:_config=) 469 @cp net/x6818-eth.mk net/eth.o
1》方法:去掉命令前边的@符号,重新执行make fs6818_defconfig
2》得到以下信息:mkconfig -A fs6818
3》因此可知:
$(MKCONFIG):在uboot源码目录下有一个mkconfig文件
$(@:_config=):截取 _config 之前的字符串
4》使用file命令查看mkconfig文件的书香,可知是一个shell脚本文件
mkconfig -A fs6818
$0 $1 $2
$#:参数的个数
2、打开uboot源码顶层目录下的mkconfig文件
24 if [ \( $# -eq 2 \) -a \( "$1" = "-A" \) ] ; then
25 # Automatic mode
26 line=`awk '($0 !~ /^#/ && $7 ~ /^'"$2"'$/) { print $1, $2, $3, $4, $5, $6, $7, $8 }' $srctree/boards.cfg`
解析:从board.s文件中找到不是以#号开头的,并且$7等于<board_name>的行,将这一行的内容赋值给line变量
27 if [ -z "$line" ] ; then
28 echo "make: *** No rule to make target \`$2_config'. Stop." >&2
29 exit 1
30 fi
解析:不成立
32 set ${line}
解析:重新设置给mkconfig传递的参数,重新设置之后变成:
mkconfig fs6818
$8
33 # add default board name if needed
34 [ $# = 3 ] && set ${line} ${1}
35 fi
Active arm slsiap s5p6818 s5p6818 fs6818 fs6818 - $1 $2 $3 $4 $5 $6 $7 $8
51 CONFIG_NAME="${7%_config}"
解析:CONFIG_NAME=fs6818
52
53 [ "${BOARD_NAME}" ] || BOARD_NAME="${7%_config}"
解析:BOARD_NAME = fs6818
54
55 arch="$2"
解析:arch = arm
56 cpu=`echo $3 | awk 'BEGIN {FS = ":"} ; {print $1}'`
解析:cpu = slsiap
57 spl_cpu=`echo $3 | awk 'BEGIN {FS = ":"} ; {print $2}'`
解析:spl_cpu =
58
59 if [ "$cpu" = "-" ] ; then
60 cpu=
61 fi
62
63 [ "$6" != "-" ] && board="$6"
解析:board = fs6818
64 [ "$5" != "-" ] && vendor="$5"
解析:vendor = s5p6818
65 [ "$4" != "-" ] && soc="$4"
解析:soc = s5p6818
126 # 127 # Create include file for Make
解析:创建给Makefile使用的头文件
129 ( echo "ARCH = ${arch}"
130 if [ ! -z "$spl_cpu" ] ; then
131 echo 'ifeq ($(CONFIG_SPL_BUILD),y)'
132 echo "CPU = ${spl_cpu}"
133 echo "else"
134 echo "CPU = ${cpu}"
135 echo "endif"
136 else
137 echo "CPU = ${cpu}"
138 fi
139 echo "BOARD = ${board}"
140
141 [ "${vendor}" ] && echo "VENDOR = ${vendor}"
142 [ "${soc}" ] && echo "SOC = ${soc}"
143 exit 0 ) > config.mk
解析:根据以上变量的定义,通过echo输出重定向到config.mk
打开include/config.mk文件,得到以下信息:
1 ARCH = arm
2 CPU = slsiap
3 BOARD = fs6818
4 VENDOR = s5p6818
5 SOC = s5p6818
config.mk是Makefile的一个头文件,Makfile根据 config.mk中变量的定义,会有选择性的对硬件相关的代码进行编译。
153 # Create board specific header file
解析:创建板子指定的头文件config.h文件
154 #
155 if [ "$APPEND" = "yes" ] # Append to existing config file
156 then
157 echo >> config.h
158 else
159 > config.h # Create new config file
160 fi
161 echo "/* Automatically generated - do not edit */" >>config.h
162
163 for i in ${TARGETS} ; do
164 i="`echo ${i} | sed '/=/ {s/=/ /;q; } ; { s/$/ 1/; }'`"
165 echo "#define CONFIG_${i}" >>config.h ;
166 done
167
168 echo "#define CONFIG_SYS_ARCH \"${arch}\"" >> config.h
169 echo "#define CONFIG_SYS_CPU \"${cpu}\"" >> config.h
170 echo "#define CONFIG_SYS_BOARD \"${board}\"" >> config.h
171
172 [ "${vendor}" ] && echo "#define CONFIG_SYS_VENDOR \"${vendor}\"" >> config.h
173
174 [ "${soc}" ] && echo "#define CONFIG_SYS_SOC \"${soc}\"" >> config.h
175
176 [ "${board}" ] && echo "#define CONFIG_BOARDDIR board/$BOARDDIR" >> config.h
177 cat << EOF >> config.h
178 #include <config_cmd_defaults.h>
179 #include <config_defaults.h>
180 #include <configs/${CONFIG_NAME}.h>
181 #include <asm/config.h>
解析:将一些宏定义和头文件追加到config.h文件
2 #define CONFIG_SYS_ARCH "arm"
3 #define CONFIG_SYS_CPU "slsiap"
4 #define CONFIG_SYS_BOARD "fs6818"
5 #define CONFIG_SYS_VENDOR "s5p6818"
6 #define CONFIG_SYS_SOC "s5p6818"
7 #define CONFIG_BOARDDIR board/s5p6818/fs6818
8 #include <config_cmd_defaults.h>
9 #include <config_defaults.h>
10 #include <configs/fs6818.h>
11 #include <asm/config.h>
12 #include <config_fallbacks.h>
13 #include <config_uncmd_spl.h>
【7】make all执行过程详解ubootpak.bin
1、打开Makefile文件,搜索all,得到以下信息
764 all: $(ALL-y)
2、搜索ALL-y变量如何定义,得到以下信息
712 ALL-y += u-boot.srec u-boot.bin System.map binary_size_check
3、搜素u-boot.bin,得到以下信息
804 u-boot.bin: u-boot FORCE
805 $(call if_changed,objcopy)
806 $(callDO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE))
807 $(BOARD_SIZE_CHECK)
808 ./tools/mk6818 ubootpak.bin nsih.txt 2ndboot u-boot.bin
解析:使用file查看mk6818文件的属性,可知是一个可执行文件
mk6818文件的作用:将nish.txt和2ndboot,uboot.bin合并生成ubootpak.bin文件
nsih.txt:三星公司提供的源码,不开源,是一个机器码
2ndboot:三星公司提供的源码 ,不开源,是一个机器码
在终端输入命令:hexdump 2ndboot可以查看是0101的机器码
u-boot.bin:u-boot源码编译生成的二进制文件
三、linux内核的移植
【1】linux内核的特点
1)linux内核是开源的
2)linux内核支持多种硬件架构平台
3)linux内核采用模块化方式编写,分层思想
4)linux内核使用c语言和汇编代码实现
5)linux内核具有很好的移植和裁剪特性,通过图形化界面的配置方式就可以完成裁剪
【2】linux内核源码的获取
1)linux官方获取
https://mirrors.edge.kernel.org/pub/linux/kernel/
2)芯片厂家获取
3)开发板厂家获取
4)公司主管获取
5)由于三星没有将s5p6818芯片硬件支持开源到linux内核官方
6)本次课程不使用linux
【3】linux内核的版本命名方式
【4】对linux内核源码进行分析
1、拷贝内核源码到Ubuntu中
2、分析linux内核源码的目录结构
平台相关(跟硬件相关):arch
平台无关(跟硬件无关):drivers、include
3、获取linux内核源码的配置和编译的帮助信息(通过make)
通过make help 或者README
1)清除目标:clean
2)配置目标:menuconfig ->基于图形化界面配置
3)编译内核源码:modules->采用模块化进行编译
4)生成目标:uImage->编译源码得到uImage镜像
5)配置linux内核源码支持FS6818硬件平台 fs6818_defconfig
4、对linux内核源码进行配置和编译
1)修改内核源码顶层目录的Makefile文件
2)执行make clean 清除中间文件
3)执行make fs6818_defconfig,配置源码支持的FS6818平台
4)执行make menuconfig对内核源码进行图形化界面的配置,去掉研发中心在内核中添加的驱动代码
5)使用make uImage对内核源码进行编译,可以得到uImage的镜像文件
6)对uImage镜像文件进行测试
【5】编译内核错误解决方法
1、make menuconfig
第一次使用make menuconfig 需要安装图形化界面的工具
配置之前需要安装图形图(make meuconfig):
sudo apt-get install libncurses5-dev
sudo apt-get install lib32z1 (64位系统需要安装兼容库)
2、make menuconfig
scripts/kconfig/mconf Kconfig
Your display is too small to run Menuconfig!
It must be at least 19 lines by 80 columns.
make[1]: *** [menuconfig] Error 1
make: *** [menuconfig] Error 2
终端字体太大,缩小终端字体
3、make uImage
在编译的过程中可能出现如下错误:
"mkimage" command not found - U-Boot images will not be built
make[1]: *** [arch/arm/boot/uImage] Error 1
make: *** [uImage] Error 2
错误的原因:找不到mkimage命令,
根据提示分析出来mkimage应该存在uboot源码目录中
uboot源码必须进行编译之后才会有mkimage可执行程序
解决问题的方法:
将uboot源码的tools目录下的mkimage,
拷贝到到ubuntu的/usr/bin目录下:
sudo cp ~/uboot源码目录/tools/mkimage /usr/bin
uboot源码目录 ubuntu目录
4、make uImage成功的现象
得到以下信息表示编译成功:
UIMAGE arch/arm/boot/uImage
Image Name: Linux-3.4.39-farsight
Created: Mon Apr 12 12:10:29 2021
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 5391520 Bytes = 5265.16 kB = 5.14 MB
Load Address: 40008000
Entry Point: 40008000
Image arch/arm/boot/uImage is ready
【6】make {platform}_defconfig执行过程详解
1、打开内核源码顶层的Makefile,搜索%config,得到以下信息
491 %config: scripts_basic outputmakefile FORCE
492 $(Q)mkdir -p include/linux include/config
493 $(Q)$(MAKE) $(build)=scripts/kconfig $@
解析:
1. $(Q) ---> @
2.去掉 $(Q),重新执行make fs6818_defconfig,
得到以下内容
make -f scripts/Makefile.build obj=scripts/kconfig fs6818_defconfig
3.$(MAKE):make
4.$(build):-f scripts/Makefile.build obj
5.$@:fs6818_defconfig
在scripts/kconfig目录下的Makfile文件,继续执行make fs6818_defconfig
2、打开scripts/kconfig目录下的Makfile文件
95 %_defconfig: $(obj)/conf96 $(Q)$< --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)解析:$(obj):scripts/kconfig$<:第一个依赖文件:scripts/kconfig/conf$(SRCARCH):arm$@:fs6818_defconfig $(Kconfig):Kconfig(内核源码顶层目录下的Kconfig)
3、通过file命令产科conf文件的属性
可知conf是一个可执行文件conf可执行文件是根据arch/arm/config目录下的配置文件fs6818_defconfig和内核源码顶层目录下的Kconfig配置文件,最终在内核源码的顶层目录下生成一个.config的配置文件
【7】make menuconfig执行过程详解
1、打开内核源码顶层的Makefile,搜索%config,得到以下信息:
491 %config: scripts_basic outputmakefile FORCE
492 $(Q)mkdir -p include/linux include/config
493 $(Q)$(MAKE) $(build)=scripts/kconfig $@
解析:
$(Q) --> @
去掉$(Q),重新执行make menuconfig,得到以下内容:
make -f scripts/Makefile.build obj=scripts/kconfig menuconfig
$(MAKE) --> make
$(build) --> -f scripts/Makefile.build obj
$@ --> menuconfig
在scripts/kconfig目录下的Makefile文件,
继续执行make menuconfig
2、打开scripts/kconfig目录下的Makefile,
搜索menuconfig,得到以下信息:
20 menuconfig: $(obj)/mconf
21 $< $(Kconfig)
解析:
menuconfig: scripts/kconfig/mconf scripts/kconfig/mconf Kconfig
3、通过file命令查看mconf文件的属性
可知mconf是一个可执行文件,
mconf可执行文件根据内核源码顶层目录下的Kconfig配置文件,最终生成了基于菜单选项的图形化界面。
4、打开Kconfig文件,分析Kconfig文件中的内容
mainmenu "Linux/$ARCH $KERNELVERSION Kernel Configuration"
config SRCARCH
string
option env="SRCARCH"
source "arch/$SRCARCH/Kconfig"
解析:
mainmenu:后面跟的是主菜单
source:包含下一级Kconfig
主菜单:包含子菜单或菜单选项
子菜单:包含子子菜单或菜单选项
菜单选项:不能包含
5、打开arch/arm目录下的Kconfig文件
254 menu "System Type"
255
256 config MMU
257 bool "MMU-based Paged Memory Management Support"
258 default y
259 help
260 Select if you want MMU-based virtualised addressing space
261 support by paged memory management. If unsure, say 'Y'.
source “下一级Kconfig”
解析Kconfig文件中的关键字的使用
1.mainmenu:包含主菜单
格式:mainmenu “主菜单的名字”
2.menu.....endmenu :包含子菜单
格式:menu "子菜单的名字"
endmenu
3.source:包含下一级的Kconfig
格式:source “下一级Kconfig的路径”
4.bool:包含菜单选项
格式:bool “菜单选项的名字”
5.default:默认的选项
6.help:帮助信息
7.config:配置选项,写到.config中的信息
通过图形化界面菜单选项的方式进行配置时,最终修改的是.config文件的配置信息
举例:将Kconfig文件中的config MMU,
将CONFIG_MMU写到.config文件中
如果config MMU对应的菜单选项处于被选择的状态
则CONFIG_MMU=y被写到.config文件中
如有config MMU对应的菜单选项处于未被选择的状态
则# CONFIG_MMU is not set被写到.config文件中,
8..config文件中的配置信息给Makefile使用
【7】以研发中心提供的ADC的驱动程序为例进行分析
1、Kconfig
【8】添加自己的驱动到内核中,采用图形化界面进行配置
1、去掉研发中心提供的驱动程序
Device Drivers --->
Character devices --->
[ ] FS6818 beep driver
FS6818 board device driver support --->
[ ] adc driver for farsight FS6818 all platform
[ ] pwm timer driver for farsight FS6818
[ ] DS18B20 driver for farsight FS6818
FS6818 extension device driver support --->
< > This is FS6818_LED!
[ ] zlg7290 driver support input device
把所有菜单选项中的"*"号全部去掉。
2、拷贝led灯的驱动文件到内核drivers/char目录下
3、添加图形化界面的菜单选项
修改drivers/char目录下的Kconfig文件,添加一下内容
6
7 config HQYJ_LED
8 bool "HQYJ LED DRIVER"
9 default y
10 help
11 led driver test!
4、修改drivers/char目录下的Makefile文件,添加以下信息
5 obj-$(CONFIG_HQYJ_LED) += fs6818_led.o
5、通过图形化界面进行配置
Device Drivers ---> Character devices ---> [*] HQYJ LED DRIVER
6、编译内核源码,并进行测试
1> 编译内核源码 make -j4 uImage 2> 拷贝uImage到~/tftpboot目录下 3> 采用开发阶段的系统启动方式启动开发板 4> 编译led灯驱动对应的应用程序 1)拷贝led灯的应用程序到ubuntu中 2) 使用交叉编译工具链对led应用程序进行编译 arm-none-linux-gnueabi-gcc fs6818led_test.c -o led_test 3) 把led_test可执行程序拷贝到根文件系统的home目录下 cp led_test ~/nfs/rootfs/home/5> 运行led灯的应用程序,观察现象 ./led_test
【9】采用模块化的方式编译驱动
bool使用方法
[*] -> 驱动被编译到uImage内核镜像中[ ] -> 驱动不被编译到uImage内核镜像中
tristate三态使用方法
<*> -> 驱动被编译到uImage内核镜像中
< > -> 驱动不被编译到uImage内核镜像中
<M> -> 采用模块化的方式对驱动进行编译,驱动的源文件会被编译成****.ko文件
需要使用这个驱动时,使用insmod命令进行加载驱动到内核中,
insmod ****.ko
不需要使用这个驱动时,使用rmmod命令将驱动从内核中卸载,
rmmod ****
1、去掉研发中心提供的驱动程序
Device Drivers --->
Character devices --->
[ ] FS6818 beep driver
FS6818 board device driver support --->
[ ] adc driver for farsight FS6818 all platform
[ ] pwm timer driver for farsight FS6818
[ ] DS18B20 driver for farsight FS6818
FS6818 extension device driver support --->
< > This is FS6818_LED!
[ ] zlg7290 driver support input device
把所有菜单选项中的"*"号全部去掉。
2、拷贝led灯的驱动文件到内核drivers/char目录下
3、添加图形化界面的菜单选项
修改drivers/char目录下的Kconfig文件,添加一下内容
6
7 config HQYJ_LED
8 tristate "HQYJ LED DRIVER"
9 default y
10 help
11 led driver test!
4、修改drivers/char目录下的Makefile文件,添加以下信息
5 obj-$(CONFIG_HQYJ_LED) += fs6818_led.o
5、通过图形化界面进行配置
Device Drivers --->
Character devices --->
<M> HQYJ LED DRIVER
6、编译内核源码,并进行测试
1> 编译内核源码
make modules
得到以下信息表示编译成功:
CC [M] drivers/char/fs6818_led.o
LD [M] drivers/char/fs6818_led.ko
2> 拷贝fs6818_led.ko到跟文件系统的home目录下
3> 使用insmod 加载驱动
# cd home
# insmod fs6818_led.ko
4> 编译led灯驱动对应的应用程序
1)拷贝led灯的应用程序到ubuntu中
2) 使用交叉编译工具链对led应用程序进行编译
arm-none-linux-gnueabi-gcc fs6818led_test.c -o led_test
3) 把led_test可执行程序拷贝到根文件系统的home目录下
cp led_test ~/nfs/rootfs/home/
5> 运行led灯的应用程序,观察现象
./led_test
6> 卸载led灯的驱动
# rmmod fs6818_led
出现以下错误:
rmmod: can't change directory to '/lib/modules': No such file or directory
解决办法:在跟文件系统的lib目录下创建modules
# cd lib
# mkdir modules
出现以下错误:
rmmod: can't change directory to '3.4.39-farsight': No such file or directory
解决办法:在跟文件系统的lib/modules目录下创建3.4.39-farsight
# cd lib/modules
# mkdir 3.4.39-farsight
四、根文件系统的移植
参考项目的部署
五、思考
【1】u-boot有几种启动模式
1、交互模式:倒计时减到0之前,按下任意键进入交互模式,此时可以输入各种u-boot命令,跟u-boot进行交互
2、自启动模式:倒计时减到0之前,不按下任意键,自动进入自启动模式,执行boot传递的后边命令
3、bootcmd:内核自启动命令
4、bootargs:内核自启动参数
【2】u-boot的启动流程
1、内存映射图
作用:告诉每块空间做什么事情
SRAM:内部的静态RAM
Normal l/O:特殊功能寄存器空间
MCU-A(2GB)DDR:s5p6818最多外接5GB的DDR内存条,而fs6818开发板外接1G的DDR内存寻址空间为0x40000000 - 0x80000000
ROM:内部的iROM,iROM是一块只读存储区,掉电不丢失,只允许被写入一次数据,芯片厂家生成芯片时,在iROM固化了一段启动代码,不开源,上电先执行iROM,然后再ubootpak.bin
查看芯片手册第三章
1)开发板上电,启动iROM固化代码,根据拨码开关高低电平的状态,判断u-boot的启动方式 on表示高电平,off表示低电平
SDHC #0 : 表示SD卡
SDHC #2 : 表示EMMC
2)跳过SD/EMMC前512字节,也就是跳过分区表,拷贝ubootpak.bin前56k的代码到内部的SRAM中
3)PC从内部的iROM指向SRAM中ubootpak.bin的第一行代码,继续执行
4)SRAM中的代码执行,搬移ubootpak.bin到外接的DDR内存中,PC在执行内存中ubootpak.bin的第一行代码,执行ubootpak.bin
2、BootLoader分解
1)b10:固化在iROM中的代码
作用:检查u-boot的启动方式,完成对SRAM的初始化,搬移ubootpak.bin前56K到SRAM中,让PC指向SRAM的第一行代码
2)bl1:nsih.txt + 2ndboot
作用:完成部分硬件的初始化,比如时钟、内存、串口
完成u-boot的搬移,将u-boot搬移到内存中,让PC指向内存中的u-boot的第一行代码继续执行
3)bl2:u-boot.bin
完整大部分硬件的初始化,比如时钟,串口,内存,网卡,EMMC初始化,电源等等
根据用户的选择可以进入uboot的交互模式或者是自启动模式,引导加载linux内核的启动,并且给内核传递参数
uboot的启动流程?
1.系统上电
2.启动BL0,BL0检查u-boot的启动方式,完成SRAM的初始化
3.搬移ubootpak.bin的奇案56K到SRAM
4.将PC指向SRAM中的第一行代码
5.BL1启动是继续完成硬件的初始化,比如内存,时钟
6.BL1搬移到BL2内存中,跳转到BL2的第一行代码继续执行
7.BL2启动,完成大部分硬件初始,比如时钟,内存,网卡,EMMC,电源,串口
8.根据用户的请求,进入交互模式或者是自启动模式
【3】u-boot在启动的过程主要做了哪些工作
思路:分析u-boot代码的执行过程
找到u-boot执行的第一条汇编指令
(u-boot.lds)链接脚本作用:代码如何进行编译
1、分析u-boot源码中u-boot.lds链接脚本文件
通过链接脚本文件,可知代码段的第一个文件
2、打开文件 arch/arm/cpu/slsiap/s5p6818/start.S
3、执行第一条代码 b reset
3、执行跳转函数 bl cpu_init_cp15
4、cpu_init_cp15函数执行内容
5、cpu_init_crit函数执行内容
6、跳转到lowlevel_init函数执行内容
7、mov pc,lr继续回去执行start.S文件执行内容
8、board_init_r函数执行内容
u-boot在启动时,主要做了哪些工作
第一阶段:汇编阶段
1.构成异常向量表
2.禁止MMU和cache,禁止看门狗
3.硬件时钟的初始,内存的初始化
4.清除bss段
5.完成u-boot代码的自搬移
6.初始化c代码的栈空间
第二阶段:C阶段
1.完成大部分硬件的初始化
2.串口的初始化
3.内存的进一步的初始化
4.电源的初始化,等等必要硬件的初始化
5.根据命令是否进入交互模式还是自启动模式
6.获取u-boot的环境变量
7.执行bootcmd中的命令
8.最终给内核传递参数bootargs
【4】makefile、.config、kconfig之间的关系
1、make fs6818_defconfig ---->.config
conf可执行程序根据arch/arm/configs目录下的缺省配置文件fs6818_defconfig和内核源码顶层目录下的Kconfig文件,在内核源码顶层目录下生成.config
2、make menuconfig---->.config
基于图形化界面的菜单选项,mconf可执行程序解析内核源码顶层目录下的Kconfig文件,生成图形化界面菜单选项配置界面
【*】CONFIG ***=y被写到config文件中
【 】#CONFIG *** is not set被写入到.config文件中
3、.config文件是Makefile使用
Makefile根据.config文件中的变量的定义,最终决定哪些源文件被编译到uImage中,哪些不被编译
4、vmlinux:编译内核生成的elf的可执行文件
5、zImage:对vmlinux进行压缩得到
6、uImage:在zlmage前追加64字节的文件头
zlmage : vmlinux(很多个.o文件)$(obj-y)
ulmage :zlmage
obj-$(CONFIG_***) += ***.O
......
obj-$(CONFIG_***) += ***.O