基于ARM处理器的U-BOOT详细移植总结

本文深入解析了U-Boot的开发阶段,包括bootloader下载、启动方式、内存分布、关键数据结构和初始化流程。重点讲解了NANDFlash操作、环境变量初始化、网络设置和U-Boot下载模式,适合了解嵌入式开发的读者。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        一个嵌入式产品的开发阶段,需要不断地把bootloader下载到存储器中,如果存储器使用nand flash,但是第一次里面什么都没有,所以只能根据处理器的启动方式从其他方式启动如sd卡或nor存储器启动,然后在SD卡或nor存储器启动的基础之上使用USB或网络接口把u-boot.bin先下载到内存中,然后再把内存中的内容写到nand中,但是写前4页时只能写每页的前2KB数据(对于OK6410开发板来说,处理器使用S3C6410处理器,nand使用每页4KB的存储器,当从nand启动时,处理器会自动地把nand的前4页的每一页的前2KB拷贝片内8KB的SRAM中运行,这是处理器硬件所决定,所以这里只能存每一页的前2KB),前4页后的所有页都是全写。

        由于u-boot开发中需要不断调试u-boot,而此时nand中已经有u-boot,所以可以从nand存储器启动,然后根据开发的下载模式下的菜单选项,可以重新下载u-boot到nand中。

        当u-boot开发好后,并且从nand启动,一上电,处理器硬件会自动把nand flash的前4页中每页前2KB拷贝到片内SRAM中运行,而在SRAM中运行的代码又实现了把nand中从0到240KB(这个大小可以变)的代码拷贝到内存,然后跳到内存中运行,它在内存运行时,会申请更多的空间(除开本身占用的内存空间外,会包括12字节用于abort异常、堆栈空间、malloc内存池空间、环境参数空间、某些全局变量空间),总共2MB,详细查看(三.2.1)u-boot内存分布图。

        一个嵌入式产品出厂时,在nand 存储器里面已经有了u-boot、内核、文件系统,并能实现对应功能(如果使用nand存储器,那么编译好的映像是存储在nand中的,运行是在内存中运行,对于S3C6410,先在8KB片内SRAM运行,然后才跳到内存DDR中运行)。U-boot运行时会把nand里的内核映像拷贝到内存,然后运行内核。

一、U-Boot-1.1.6顶层Makefile文件分析

       根据uboot根目录下的Readme文件的说明,可以知道如果想把u-boot使用于开发板,应先配置,即执行make orlinx_nand_ram256_config命令进行配置(在顶层目录Makefile中加入forlinx_nand_ram256_config目标等选项),然后执行make all,就可以生成如下3个文件:U-Boot.bin、U-Boot ELF格式文件、U-Boot.srec。

  1. U-Boot的配置过程

        (1)版本说明

VERSION = 1
PATCHLEVEL = 1
SUBLEVEL = 6
EXTRAVERSION =
U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
VERSION_FILE = $(obj)include/version_autogenerated.h

(2)定义主机系统架构

HOSTARCH := $(shell uname -m | \
	sed -e s/i.86/i386/ \
	    -e s/sun4u/sparc64/ \
	    -e s/arm.*/arm/ \
	    -e s/sa110/arm/ \
	    -e s/powerpc/ppc/ \
	    -e s/macppc/ppc/)

        “sed  –e”表示后面跟的是一串命令脚本,而表达式“s/abc/def/”表示要从标准输入中,查找到内容为“abc”的,然后替换成“def”。其中“abc”表达式用可以使用“.”作为通配符。命令“uname –m”将输出主机 CPU 的体系架构类型。如电脑使用 Intel Core2 系列的CPU,那么 “uname  –m”将输出“i686”。 “i686”可以匹配命令“sed  -e s/i.86/i386/”中的“i.86”,因此在机器上执行 Makefile,HOSTARCH 将被设置成“i386” 。

(3)定义主机操作系统类型

HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
	    sed -e 's/\(cygwin\).*/cygwin/')
export	HOSTARCH HOSTOS
# Deal with colliding definitions from tcsh etc.
VENDOR=

        “uname  –s”输出主机内核名字,开发主机使用 Linux 发行版 fedora-12,因此“uname  –s”结果是“Linux”。“tr '[:upper:]' '[:lower:]'”作用是将标准输入中的所有大写字母转换为响应的小写字母。因此执行结果是将 HOSTOS 设置为“linux”。

(4)定义执行shell脚本的shell(源码中没有这部分)

# Set shell to bash if possible, otherwise fall back to sh
SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
else if [ -x /bin/bash ]; then echo /bin/bash; \
else echo sh; fi; fi)

        "$$BASH"的作用实质上是生成了字符串“$BASH”(前一个$号的作用是指明第二个$是普通的字符)。若执行当前 Makefile 的 shell 中定义了“$BASH”环境变量,且文件“$BASH”是可执行文件,则 SHELL 的值为“$BASH”。否则,若“/bin/bash”是可执行文件,则 SHELL 值为“/bin/bash”。若以上两条都不成立,则将“sh”赋值给 SHELL 变量。如果机器安装了bash  shell,且shell 默认环境变量中定义了“$BASH”,因此 SHELL 被设置为$BASH 。

(5)设定编译输出目录

ifdef O 
ifeq ("$(origin O)", "command line")
BUILD_DIR := $(O)
endif
endif

        函数$( origin, variable) 输出的结果是一个字符串,输出结果由变量 variable 定义的方式决定,若 variable 在命令行中定义过,则origin函数返回值为"command line"。假若在命令行中执行了“export BUILD_DIR=/tmp/build”的命令,则“$(origin O)”值为“command line”,而 BUILD_DIR 被设置为“/tmp/build”。

        下面内容表示若${BUILD_DIR}表示的目录没有定义,则创建该目录:

ifneq ($(BUILD_DIR),)
saved-output := $(BUILD_DIR)
# Attempt to create a output directory.
$(shell [ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR})

        下面内容表示若$(BUILD_DIR)为空,则将其赋值为当前目录路径(源代码目录)。并检查$(BUILD_DIR)目录是否存在:

# Verify if it was successful.
BUILD_DIR := $(shell cd $(BUILD_DIR) && /bin/pwd)
$(if $(BUILD_DIR),,$(error output directory "$(saved-output)" does not exist))
endif # ifneq ($(BUILD_DIR),)

        在下面内容中,CURDIR 变量指示 Make 当前的工作目录,由于当前 Make 在 U-Boot 顶层目录执行 Makefile,因此 CURDIR 此时就是 U-Boot 顶层目录。执行完下面的代码后, SRCTREE,src变量就是U-Boot代码顶层目录,而OBJTREE,obj变量就是输出目录,若没有定义BUILD_DIR环境变量,则SRCTREE, src变量与OBJTREE,obj变量都是U-Boot 源代码目录。而MKCONFIG则表示U-Boot根目录下的mkconfig 脚本。

OBJTREE		:= $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))
SRCTREE		:= $(CURDIR)
TOPDIR		:= $(SRCTREE)
LNDIR		:= $(OBJTREE)
export	TOPDIR SRCTREE OBJTREE

MKCONFIG	:= $(SRCTREE)/mkconfig
export MKCONFIG

ifneq ($(OBJTREE),$(SRCTREE))
REMOTE_BUILD 	:= 1
export REMOTE_BUILD
endif

# $(obj) and (src) are defined in config.mk but here in main Makefile
# we also need them before config.mk is included which is the case for
# some targets like unconfig, clean, clobber, distclean, etc.
ifneq ($(OBJTREE),$(SRCTREE))
obj := $(OBJTREE)/
src := $(SRCTREE)/
else
obj :=
src :=
endif
export obj src

        在上面内容中,MKCONFIG      := $(SRCTREE)/mkconfig即在根目录下的mkconfig文件。

6)执行make  forlinx_nand_ram256_config过程

        分析这个过程有助于理解移植U-Boot过程中需要修改哪些文件。执行这个命令前提是在移植U-Boot时,在根目录的Makefile中加入了类似如下的内容:

forlinx_nand_ram256_config :  unconfig
	@$(MKCONFIG)  smdk6410  arm  s3c64xx  smdk6410  samsung  s3c6410  NAND  ram256
	其中的依赖“unconfig”定义如下(Makefile文件的330-350行左右):
unconfig:
	@rm -f $(obj)include/config.h $(obj)include/config.mk \
		$(obj)board/*/config.tmp $(obj)board/*/*/config.tmp

        其中“@”的作用是执行该命令时不在 shell 显示。“obj”变量就是编译输出的目录,因此“unconfig”的作用就是清除上次执行 make *_config 命令生成的配置文件(如include/config.h,include/config.mk 等)。

        $(MKCONFIG)在上面(5)指定为“$(SRCTREE)/mkconfig”,即根目录的mkconfig文件。如果有$(@:_config=)一项,$(@:_config=)为将传进来的所有参数中的_config 替换为空(其中“@”指规则的目标文件名,在这里就是“forlinx_nand_ram256_config ”。$(text:patternA=patternB),这样的语法表示把 text 变量每一个元素中结尾的 patternA 的文本替换为 patternB,然后输出)。因此$(@:_config=)的作用就是将forlinx_nand_ram256_config中的_config 去掉,得到 forlinx_nand_ram256,而在OK6410移植过的U-BOOT中没有$(@:_config=)项。

    根据以上分析,执行完make forlinx_nand_ram256_config,实际上执行如下命令:

./mkconfig  smdk6410  arm  s3c64xx  smdk6410  samsung  s3c6410  NAND  ram256

        所以执行make forlinx_nand_ram256_config即将“smdk6410  arm  s3c64xx  smdk6410  samsung  s3c6410  NAND  ram256”作为参数传递给当前目录下的mkconfig 脚本执行。这些参数实际意义如下:

smdk6410:Target(目标板型号)

arm:Architecture (目标板的CPU架构)

s3c64xx:CPU(具体使用的 CPU型号)

smdk6410:Board

samsung:VENDOR(生产厂家名)

s3c6410:SOC

NAND:

ram256:

在mkconfig文件中,将进行如下几点的工作:

  1. 确定开发板名称BOARD_NAME
APPEND=no  		# no 表示创建新的配置文件,yes 表示追加到配置文件中
BOARD_NAME=""  	# Name to print in make output
TARGETS=""
while [ $# -gt 0 ] ; do
case "$1" in
--) shift ; break ;;
-a) shift ; APPEND=yes ;;
-n) shift ; BOARD_NAME="${1%%_config}" ; shift ;;
-t) shift ; TARGETS="`echo $1 | sed 's:_: :g'` ${TARGETS}" ; shift ;;
*) break ;;
esac
done
[ "${BOARD_NAME}" ] || BOARD_NAME ="$1"

        对于命令./mkconfig  smdk6410  arm  s3c64xx  smdk6410  samsung  s3c6410  NAND  ram256其中没有“--”“-a”“-n”“-t”“*”符号,所以while循环里面没有做任何事情。而头两行中的两个值仍然维持原来的值,但执行完最后一行后,BOARD_NAME的值等于第1个参数,即smdk6410(传进的几个参数在mkconfig文件中以$x表示,$0= mkconfig$1= smdk6410$2= arm$3= s3c64xx$4= smdk6410$5= samsung$6= s3c6410$7= NAND$8= ram256)

注意:

        在U-Boot1.1.6-for-OK6410源代码中BOARD_NAME=""一行后还有一行SETMMU="no",表示在nand启动是用mmu,只是这里先把值设置为no

  1. 检查参数合法性

[ $# -lt 4 ] && exit 1

[ $# -gt 9 ] && exit 1

echo "Configuring for ${BOARD_NAME} board which boot from $7 $8 $9..."

    上面代码的作用是检查参数个数和参数是否正确,参数个数少于 4 个或多于9个都被认为是错误的。环境变量$#表示传递给脚本的参数个数,这里的命令有9个参数,因此$#是9。Configuring for ${BOARD_NAME} board which boot from $7 $8 $9...即执行完make forlinx_nand_ram256_config命令后串口终端输出的信息。

  1. 创建到平台/开发板相关的头文件的符号连接
# Create link to architecture specific headers
if [ "$SRCTREE" != "$OBJTREE" ] ; then
	mkdir -p ${OBJTREE}/include
	mkdir -p ${OBJTREE}/include2
	cd ${OBJTREE}/include2
	rm -f asm
	ln -s ${SRCTREE}/include/asm-$2 asm
	LNPREFIX="../../include2/asm/"
	cd ../include
	rm -rf asm-$2
	rm -f asm
	mkdir asm-$2
	ln -s asm-$2 asm
else
	cd ./include
	rm -f asm
	ln -s asm-$2 asm  # 符号连接,即软链接
fi

        第一行代码判断源代码目录和目标文件目录是否一样,可以选择在其他目录下编译U-BOOT,这可令源代码保持干净,可以同时使用不同的配置进行编译。OK6410的U-BOOT移植过的源代码是在源代码目录下编译的,所以源代码目录等于目标文件目录,所以条件不满足,将执行else分支的代码。

        在else分支的代码中,先进入include目录,删除asm文件(这是上一次配置时建立的链接文件),然后再次建立asm文件,并令它链接向asm-$2目录,即asm-arm目录。此举的作用为:在源码中常调用头文件,如#include <asm-arm/type.h>,对不同架构需要修改”asm-XXX架构”,先建立asm到asm-arm的符号链接后,以后包含头文件时直接包含

1:rm -f asm-$2/arch
2:if [ -z "$6" -o "$6" = "NULL" ] ; then
3:		ln -s ${LNPREFIX}arch-$3 asm-$2/arch
4:else
5:		ln -s ${LNPREFIX}arch-$6 asm-$2/arch
6:fi
7:if [ "$2" = "arm" ] ; then
8:		rm -f asm-$2/proc
9:		ln -s ${LNPREFIX}proc-armv asm-$2/proc
10:fi

        第1行删除include/asm-arm/arch目录,对于命令./mkconfig  smdk6410  arm  s3c64xx  smdk6410  samsung  s3c6410  NAND  ram256$6S3C6410,不为空,也不为NULL,所以第2行条件不满足,执行else分支。第5行中,LNPREFIX为空(在顶层makefile中未定义),所以第5行即执行ln  –s  arch-s3c6410  asm-arm/arch(在U-Boot1.1.6-for-OK6410源代码中,后面又把include/asm-arm/arch链接到了include/asm-arm/arch-s3c64xx)。

       8-9行表示:若目标板是arm架构,则上面的代码将建立符号连接 include/asm-arm/proc,使其链接到目录 include/asm-arm/proc-armv 目录。建立以上的链接的好处:编译 U-Boot 时直接进入链接文件指向的目录进行编译,而不必根据不同开发板来选择不同目录。

注意:

    U-Boot1.1.6-for-OK6410源代码中,在第6行到7行之间,增加了如下部分代码for OK6410

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值