Loading

系统移植

一、环境搭建/系统部署

【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卡的启动盘

image

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  :擦除的块数

image

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链接脚本文件

通过链接脚本文件,可知代码段的第一个文件

image

2、打开文件 arch/arm/cpu/slsiap/s5p6818/start.S

image

3、执行第一条代码 b reset

image

3、执行跳转函数 bl cpu_init_cp15

image

4、cpu_init_cp15函数执行内容

image
5、cpu_init_crit函数执行内容

image

6、跳转到lowlevel_init函数执行内容

image

7、mov pc,lr继续回去执行start.S文件执行内容

image

image

image

8、board_init_r函数执行内容

image

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
posted @ 2021-06-04 22:03  Yangtai  阅读(263)  评论(0编辑  收藏  举报