uboot代码分析

开发板:JZ2440V3

uboot代码版本:u-boot-1.1.6

本文的主要内容是韦东山讲解的uboot分析笔记。如果有错误欢迎指出讨论。谢谢!

1.将源码压缩包上传服务器并解压源码:tar -xvf u-boot-1.1.6.tar.bz2;

2.打补丁:cd u-boot-1.1.6
patch -p1 < ../u-boot-1.1.6_jz2440.patch

"-p1"表示忽略第一个"/"符合之前的东西;

        补丁文件的简要介绍:

        --- u-boot-1.1.6/board/100ask24x0/100ask24x0.c 1970-01-01 07:00:00.000000000 +0700

+++ u-boot-1.1.6_jz2440/board/100ask24x0/100ask24x0.c 2010-11-26 12:54:37.034090906 +0800

        "---"表示原来的代码
"+++"表示修改后的代码

3.配置uboot:

        执行:make 100ask24x0_config

        输出:Configuring for 100ask24x0 board...

4.编译uboot:

        make

编译完成之后,使用openjtag下载uboot.bin文件看看是否能成功运行。打印信息如下:

U-Boot 1.1.6 (Apr 29 2018 - 08:50:02)

DRAM:  64 MB
Flash:  0 kB
NAND:  256 MiB
In:    serial
Out:   serial
Err:   serial
UPLLVal [M:38h,P:2h,S:2h]
MPLLVal [M:5ch,P:1h,S:1h]
CLKDIVN:5h
+---------------------------------------------+
| S3C2440A USB Downloader ver R0.03 2004 Jan  |
+---------------------------------------------+
USB: IN_ENDPOINT:1 OUT_ENDPOINT:3
FORMAT: <ADDR(DATA):4>+<SIZE(n+10):4>+<DATA:n>+<CS:2>
NOTE: Power off/on or press the reset button for 1 sec
      in order to get a valid USB device address.

Hit any key to stop autoboot:  0

说明配置并编译成功。

uboot有哪些命令,可以在倒数计时时按任意键,进入菜单选项,然后输入q退出菜单选项。再输入help或?就可以打印出你的uboot支持哪些命令了。

OpenJTAG> ?
?       - alias for 'help'
autoscr - run script from memory
base    - print or set address offset
bdinfo  - print Board Info structure
boot    - boot default, i.e., run 'bootcmd'
bootd   - boot default, i.e., run 'bootcmd'
bootelf - Boot from an ELF image in memory
bootm   - boot application image from memory
bootp   - boot image via network using BootP/TFTP protocol
bootvx  - Boot vxWorks from an ELF image
chpart  - change active partition
cmp     - memory compare
coninfo - print console devices and information
cp      - memory copy
crc32   - checksum calculation
date    - get/set/reset date & time
dcache  - enable or disable data cache
echo    - echo args to console
erase   - erase FLASH memory
flinfo  - print FLASH memory information
fsinfo  - print information about filesystems
fsload  - load binary file from a filesystem image
go      - start application at address 'addr'
help    - print online help
icache  - enable or disable instruction cache
iminfo  - print header information for application image
imls    - list all images found in flash
itest   - return true/false on integer compare
loadb   - load binary file over serial line (kermit mode)
loads   - load S-Record file over serial line
loadx   - load binary file over serial line (xmodem mode)
loady   - load binary file over serial line (ymodem mode)
loop    - infinite loop on address range
ls      - list files in a directory (default /)
md      - memory display
menu - display a menu, to select the items to do something
mm      - memory modify (auto-incrementing)
mtdparts- define flash/nand partitions
mtest   - simple RAM test
mw      - memory write (fill)
nand    - NAND sub-system
nboot   - boot from NAND device
nfs     - boot image via network using NFS protocol
nm      - memory modify (constant address)
ping    - send ICMP ECHO_REQUEST to network host
printenv- print environment variables
protect - enable or disable FLASH write protection
rarpboot- boot image via network using RARP/TFTP protocol
reset   - Perform RESET of the CPU
run     - run commands in an environment variable
saveenv - save environment variables to persistent storage
setenv  - set environment variables
sleep   - delay execution for some time
tftpboot- boot image via network using TFTP protocol
usbslave - get file from host(PC)
version - print monitor version
OpenJTAG>

如果想查看具体命令的使用说明可以使用这种方式:? 命令。比如:? md

OpenJTAG> ? md
md [.b, .w, .l] address [# of objects]
    - memory display

在这一节里我们不详细讲解uboot的命令时怎么实现的,之后我会专门写一篇文章,分析uboot中命令的实现以及常见命令的使用方法。

uboot是怎么启动内核的,uboot将内核kernel从flash上面读出放到sdram中,然后启动内核。

uboot要实现的功能:

1.能够读flash;

2.初始化sdram,初始化时钟,关闭看门狗,初始化串口。

3.启动内核。

u-boot分析之Makefile结构分析:

想分析一个文件的链接结构最简单的方法就是分析其makefile文件。

我们在编译之前要先配置,再编译,为什么要这样做呢,因为在uboot的根目录下有的README说明文件,建议大家阅读一遍会收获满满。

首先分析配置过程:

在配置时执行的是make 100ask24x0_config命令,查看根目录下的makefile文件发现:

100ask24x0_config	:	unconfig
	@$(MKCONFIG) $(@:_config=) arm arm920t 100ask24x0 NULL s3c24x0

有这样两行,起始在执行make 100ask24x0_config命令时,就相当于执行"@$(MKCONFIG) $(@:_config=) arm arm920t 100ask24x0 NULL s3c24x0"这句话,好了,我们来看看这句话都是做了哪些工作。

MKCONFIG := $(SRCTREE)/mkconfig
100ask24x0_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t 100ask24x0 NULL s3c24x0

其中,MKCONFIG代表的就是源码树目录下的mkconfig文件,此文件是个shell脚本文件。其实就相当于执行:

./mkconfig 100ask24x0 arm arm920t 100ask24x0 NULL s3c24x0 

也就是说,在配置时是执行的mkconfig脚本文件,执行脚本文件的参数是:100ask24x0 arm arm920t 100ask24x0 NULL s3c24x0。

现在我们分析mkconfig这个脚本文件,在分析时我们只分析执行到的代码,省略不执行的代码。

首先看到mkconfig文件里面前几行注释一段话:

Parameters:  Target  Architecture  CPU  	Board 	    [VENDOR]    [SOC]
	        $1         $2       $3           $4            $5         $6
            100ask24x0    arm     arm920t     100ask24x0      NULL      s3c24x0 

就是执行脚本文件的参数含义。

#!/bin/sh -e

# Script to create header files and links to configure
# U-Boot for a specific board.
#
# Parameters:  Target  Architecture  CPU     Board       [VENDOR]    [SOC]
#			100ask24x0    arm      arm920t  100ask24x0     NULL     s3c24x0 
# (C) 2002-2006 DENX Software Engineering, Wolfgang Denk <wd@denx.de>
#

APPEND=no	# Default: Create new config file
BOARD_NAME=""	# Name to print in make output

while [ $# -gt 0 ] ; do			# 如果参数个数大于0,条件成立
	case "$1" in			# $1也就是参数1有下面的符合,那就执行响应的项,没有,不执行
	--) shift ; break ;;
	-a) shift ; APPEND=yes ;;
	-n) shift ; BOARD_NAME="${1%%_config}" ; shift ;;
	*)  break ;;
	esac
done

[ "${BOARD_NAME}" ] || BOARD_NAME="$1"	# BOARD_NAME变量为空,所以BOARD_NAME=$1,也就是BOARD_NAME=100ask24x0

[ $# -lt 4 ] && exit 1		        # 如果参数个数小于4,退出
[ $# -gt 6 ] && exit 1		        # 如果参数个数大于6,退出;这个两个条件都不成立,继续往下执行

echo "Configuring for ${BOARD_NAME} board..."	# 输出打印信息:Configuring for 100ask24x0 board...

#
# Create link to architecture specific headers 建立和架构相关的头文件
#
if [ "$SRCTREE" != "$OBJTREE" ] ; then		# 这两个变量在makefile文件中定义,是相等的,所以条件不成立
	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		# 进入include目录
	rm -f asm		# 删除asm文件夹
	ln -s asm-$2 asm	# 建立链接文件:ln -s asm-arm asm 也就是asm链接asm-arm而成
fi

rm -f asm-$2/arch		# 删除rm -f asm-arm/arch文件夹

if [ -z "$6" -o "$6" = "NULL" ] ; then		# 如果$6不存在或为NULL执行下面的分支,但是条件不成立
	ln -s ${LNPREFIX}arch-$3 asm-$2/arch
else						# 执行下面的分支语句
	ln -s ${LNPREFIX}arch-$6 asm-$2/arch	# 建立链接文件,ln -s arch-s3c24x0 asm-arm/arch
fi

if [ "$2" = "arm" ] ; then			# 如果$2等于arm,成立
	rm -f asm-$2/proc			# 执行rm -f asm-arm/proc 
	ln -s ${LNPREFIX}proc-armv asm-$2/proc  # 执行ln -s proc-armv asm-arm/proc 
fi

#
# Create include file for Make 创建头文件,其中">"表示新建一个文件,">>"表示追加
#
echo "ARCH   = $2" >  config.mk		# 在include目录下新建一个config.mk文件,并将ARCH   = arm 这句话输出到里面
echo "CPU    = $3" >> config.mk		# 将CPU    = arm920t 追加到config.mk文件
echo "BOARD  = $4" >> config.mk		# 将BOARD  = 100ask24x0 追加到config.mk文件
					# 我们可以查看config.mk文件的内容,确实是这样的。

# $5为NULL,条件不成立
[ "$5" ] && [ "$5" != "NULL" ] && echo "VENDOR = $5" >> config.mk
# $6为s3c24x0条件成立,输出SOC    = s3c24x0到config.mk文件中
[ "$6" ] && [ "$6" != "NULL" ] && echo "SOC    = $6" >> config.mk

#
# Create board specific header file 创建单板相关的头文件
#
if [ "$APPEND" = "yes" ]	# Append变量为no,条件不成立,不执行下面分支的语句
then
	echo >> config.h
else				# 执行下面分支的语句
	> config.h		# 在include目录下新建一个config.h文件
fi

# 输出"/* Automatically generated - do not edit */" 到config.h文件中
echo "/* Automatically generated - do not edit */" >>config.h
# 输出"#include <configs/100ask24x0.h>"到config.h文件中
echo "#include <configs/$1.h>" >>config.h
exit 0

好了,配置过程已经分析完毕了,里面有详细的注释,不在单独详细介绍了。

现在来分析编译过程,我们在编译时直接执行的make命令,现在我们来继续分析makefile文件:

include $(OBJTREE)/include/config.mk

上面这句话就是包含配置时生产的config.mk文件,里面的内容是:

ARCH   = arm
CPU    = arm920t
BOARD  = 100ask24x0
SOC    = s3c24x0

而这些东西在makefile文件里面会用得到。

ifeq ($(ARCH),arm)
CROSS_COMPILE = arm-linux-
endif

CPU是ARM架构的,声明交叉编译工具链是"arm-linux-"。

OBJS  = cpu/arm920t/start.o 给OBJS变量赋值,这个变量很重要,继续往下看。

LIBS  = lib_generic/libgeneric.a
LIBS += board/100ask24x0/lib100ask24x0.a
LIBS += cpu/arm920t/libarm920t.a
LIBS += cpu/arm920t/s3c24x0/libs3c24x0.a
LIBS += lib_arm/libarm.a
LIBS += fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a \
		fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a
LIBS += net/libnet.a
LIBS += disk/libdisk.a
LIBS += rtc/librtc.a
LIBS += dtt/libdtt.a
LIBS += drivers/libdrivers.a
LIBS += drivers/nand/libnand.a
LIBS += drivers/nand_legacy/libnand_legacy.a
LIBS += drivers/usb/libusb.a
LIBS += drivers/sk98lin/libsk98lin.a
LIBS += common/libcommon.a

上面的意思就是将各个目录下的东西打包生成.a文件。

all:		$(ALL)

在执行make的时候,如果没有指定目标,那就执行上面这句话,也就是目标是all。依赖是$(ALL)。

ALL = $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)

目的是生成u-boot.bin文件,而u-boot.bin文件的依赖是:

$(obj)u-boot.bin:	$(obj)u-boot    # ELF格式的文件
		$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@

而u-boot的依赖是:

$(obj)u-boot:	depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)
		UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed  -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
		cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
			--start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
			-Map u-boot.map -o u-boot

但是把上面的东西每个都展开发现工作量很大,但是我们可以执行make命令,查看控制台输出:

UNDEF_SYM=`arm-linux-objdump -x lib_generic/libgeneric.a board/100ask24x0/lib100ask24x0.a 
		cpu/arm920t/libarm920t.a cpu/arm920t/s3c24x0/libs3c24x0.a lib_arm/libarm.a fs/cramfs/libcramfs.a 
		fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a 
		net/libnet.a disk/libdisk.a rtc/librtc.a dtt/libdtt.a drivers/libdrivers.a drivers/nand/libnand.a 
		drivers/nand_legacy/libnand_legacy.a drivers/usb/libusb.a drivers/sk98lin/libsk98lin.a 
		common/libcommon.a | sed  -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
arm-linux-ld -Bstatic -T /work/jz2440/u-boot-1.1.6/board/100ask24x0/u-boot.lds -Ttext 0x33F80000  
		$UNDEF_SYM cpu/arm920t/start.o \
--start-group lib_generic/libgeneric.a board/100ask24x0/lib100ask24x0.a cpu/arm920t/libarm920t.a 
		cpu/arm920t/s3c24x0/libs3c24x0.a lib_arm/libarm.a fs/cramfs/libcramfs.a fs/fat/libfat.a 
		fs/fdos/libfdos.a fs/jffs2/libjffs2.a fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a net/libnet.a 
		disk/libdisk.a rtc/librtc.a dtt/libdtt.a drivers/libdrivers.a drivers/nand/libnand.a 
		drivers/nand_legacy/libnand_legacy.a drivers/usb/libusb.a drivers/sk98lin/libsk98lin.a 
		common/libcommon.a --end-group -L /work/tools/gcc-3.4.5-glibc-2.3.6/lib/gcc/arm-linux/3.4.5 -lgcc 
		\-Map u-boot.map -o u-boot

我们可以发现链接脚本文件是:"board/100ask24x0/u-boot.lds"文件。代码段的基地址0x33F80000。链接的文件是"$UNDEF_SYM"变量代表的文件。

而第一个链接的文件时"cpu/arm920t/start.o";而uboot开始运行的地址是0x33F80000,首先运行的文件时start.S文件。

通过分析makefile文件可以知道:

1.第一个执行的文件时"cpu/arm920t/start.S";

2.链接脚本文件是"board/100ask24x0/u-boot.lds"文件;

3.链接地址是0x33F80000;这个值是在"/board/100ask24x0/config.mk"文件中定义的;变量名称是TEXT_BASE;

u-boot分析之源码第1阶段:

cpu/arm920t/start.S:

.globl _start            #一上电CPU从这里开始执行
_start:	b       reset    #跳转到reset符合处执行
	ldr	pc, _undefined_instruction    #下面这几个是其他异常向量代码,现在不分析。
	ldr	pc, _software_interrupt
	ldr	pc, _prefetch_abort
	ldr	pc, _data_abort
	ldr	pc, _not_used
	ldr	pc, _irq
	ldr	pc, _fiq

分析reset处的代码:

/*
 * the actual reset code
 */
reset:
	/*
	 * set the cpu to SVC32 mode
	 * 设置CPU为SVC模式
	 */
	mrs	r0,cpsr
	bic	r0,r0,#0x1f
	orr	r0,r0,#0xd3
	msr	cpsr,r0

/* turn off the watchdog */
#if defined(CONFIG_S3C2400)		
# define pWTCON		0x15300000
# define INTMSK		0x14400008	/* Interupt-Controller base addresses */
# define CLKDIVN	0x14800014	/* clock divisor register */
#elif defined(CONFIG_S3C2410)	        //条件成立,执行
# define pWTCON		0x53000000
# define INTMOD     0X4A000004
# define INTMSK		0x4A000008	/* Interupt-Controller base addresses */
# define INTSUBMSK	0x4A00001C
# define CLKDIVN	0x4C000014	/* clock divisor register */
#endif

#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)	//条件成立,执行
	ldr     r0, =pWTCON	
	mov     r1, #0x0
	str     r1, [r0]		                //关闭看门狗

	/*
	 * mask all IRQs by setting all bits in the INTMR - default
	 */
	mov	r1, #0xffffffff
	ldr	r0, =INTMSK
	str	r1, [r0]			//关闭所有中断
	
# if defined(CONFIG_S3C2410)
	ldr	r1, =0x3ff
	ldr	r0, =INTSUBMSK
	str	r1, [r0]			//关闭所有子中断
# endif

#endif	/* CONFIG_S3C2400 || CONFIG_S3C2410 */

什么的代码主要完成设置CPU为SVC模式,关闭看门狗,屏蔽所有中断。继续往下分析。

#ifndef CONFIG_SKIP_LOWLEVEL_INIT	//没有定义此宏,执行
	adr	r0, _start		/* r0 <- current position of code   */
	ldr	r1, _TEXT_BASE		/* test if we run from flash or RAM */
	cmp     r0, r1                  /* don't reloc during debug         */
	blne	cpu_init_crit           #如果不相等,这执行,不管是Nor或NAND启动都是不相等的,跳转到cpu_init_crit处执行代码
#endif
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
cpu_init_crit:
	/*
	 * flush v4 I/D caches 刷新I/D Caches
	 */
	mov	r0, #0
	mcr	p15, 0, r0, c7, c7, 0	/* flush v3/v4 cache */
	mcr	p15, 0, r0, c8, c7, 0	/* flush v4 TLB */

	/*
	 * disable MMU stuff and caches	     关闭MMU和Cache
	 */
	mrc	p15, 0, r0, c1, c0, 0
	bic	r0, r0, #0x00002300	@ clear bits 13, 9:8 (--V- --RS)
	bic	r0, r0, #0x00000087	@ clear bits 7, 2:0 (B--- -CAM)
	orr	r0, r0, #0x00000002	@ set bit 2 (A) Align
	orr	r0, r0, #0x00001000	@ set bit 12 (I) I-Cache
	mcr	p15, 0, r0, c1, c0, 0

	/*
	 * before relocating, we have to setup RAM timing
	 * because memory timing is board-dependend, you will
	 * find a lowlevel_init.S in your board directory.
	 */
	mov	ip, lr            #保存返回地址到ip寄存器
	bl	lowlevel_init     #跳转到lowlevel_init处执行代码   
	mov	lr, ip            #将ip保存的返回地址移动到lr寄存器中
	mov	pc, lr            #将lr中的返回地址保存到pc寄存器,也就是跳转到返回地址处执行代码
#endif /* CONFIG_SKIP_LOWLEVEL_INIT */

上面的代码主要完成了刷新I/D Caches,关闭MMU和Caches,并跳转到lowlevel_init处执行代码,而此代码在"board/100ask24x0/lowlevel_init.S"文件中定义。

.globl lowlevel_init
lowlevel_init:
	/* memory control configuration */
	/* make r0 relative the current location so that it */
	/* reads SMRDATA out of FLASH rather than memory ! */
	ldr     r0, =SMRDATA
	ldr	r1, _TEXT_BASE
	sub	r0, r0, r1
	ldr	r1, =BWSCON	/* Bus Width Status Controller */
	add     r2, r0, #13*4
0:
	ldr     r3, [r0], #4
	str     r3, [r1], #4
	cmp     r2, r0
	bne     0b

	/* everything is fine now */
	mov	pc, lr

	.ltorg
/* the literal pools origin */

SMRDATA:
    .word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28)) 
    .word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))
    .word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))
    .word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))
    .word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))
    .word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))
    .word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))
    .word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))
    .word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))
    .word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)
    .word 0xb1
    .word 0x30
    .word 0x30

上面的代码主要完成SDRAM控制器的初始化工作,这里不详细介绍。

执行完之后代码返回到下面代码处执行:

/* Set up the stack */
stack_setup:                                            /* 设置栈 */
	ldr	r0, _TEXT_BASE				/* upper 128 KiB: relocated uboot r0=0x33F80000 */
	sub	r0, r0, #CFG_MALLOC_LEN			/* malloc area   CFG_MALLOC_LEN = 0x40000 将ro减去0x40000 */
	sub	r0, r0, #CFG_GBL_DATA_SIZE 		/* bdinfo CFG_GBL_DATA_SIZE = 128  将r0减去128  */

#ifdef CONFIG_USE_IRQ					/* 有定义此宏,执行 */
	sub	r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)    #将r0减去0x2000 用作中断处理
#endif
	sub	sp, r0, #12				/* 设置栈,之后就可以跳转到C代码执行了 */

#ifndef CONFIG_SKIP_LOWLEVEL_INIT		/* 没有定义此宏,执行跳转命令 */
    bl clock_init                               /* 跳转到初始化时钟的代码处执行 */
#endif    

上面的代码主要给各种情况预留内存。内存分布如下图所示:



现在我们看clock_init出的代码,主要完成时钟的初始化;此处的代码在"board/100ask24x0/boot_init.c"文件中定义。

void clock_init(void)
{
	S3C24X0_CLOCK_POWER *clk_power = (S3C24X0_CLOCK_POWER *)0x4C000000;

    /* support both of S3C2410 and S3C2440, by www.100ask.net */
    if (isS3C2410)
    {
        /* FCLK:HCLK:PCLK = 1:2:4 */
        clk_power->CLKDIVN = S3C2410_CLKDIV;

        /* change to asynchronous bus mod */
        __asm__(    "mrc    p15, 0, r1, c1, c0, 0\n"    /* read ctrl register   */  
                    "orr    r1, r1, #0xc0000000\n"      /* Asynchronous         */  
                    "mcr    p15, 0, r1, c1, c0, 0\n"    /* write ctrl register  */  
                    :::"r1"
                    );
        
        /* to reduce PLL lock time, adjust the LOCKTIME register */
        clk_power->LOCKTIME = 0xFFFFFFFF;

        /* configure UPLL */
        clk_power->UPLLCON = S3C2410_UPLL_48MHZ;

        /* some delay between MPLL and UPLL */
        delay (4000);

        /* configure MPLL */
        clk_power->MPLLCON = S3C2410_MPLL_200MHZ;

        /* some delay between MPLL and UPLL */
        delay (8000);
    }
    else
    {
        /* FCLK:HCLK:PCLK = 1:4:8 */
        clk_power->CLKDIVN = S3C2440_CLKDIV;

        /* change to asynchronous bus mod */
        __asm__(    "mrc    p15, 0, r1, c1, c0, 0\n"    /* read ctrl register   */  
                    "orr    r1, r1, #0xc0000000\n"      /* Asynchronous         */  
                    "mcr    p15, 0, r1, c1, c0, 0\n"    /* write ctrl register  */  
                    :::"r1"
                    );

        /* to reduce PLL lock time, adjust the LOCKTIME register */
        clk_power->LOCKTIME = 0xFFFFFFFF;

        /* configure UPLL */
        clk_power->UPLLCON = S3C2440_UPLL_48MHZ;

        /* some delay between MPLL and UPLL */
        delay (4000);

        /* configure MPLL */
        clk_power->MPLLCON = S3C2440_MPLL_400MHZ;

        /* some delay between MPLL and UPLL */
        delay (8000);
    }
}

上面的代码主要完成时钟的初始化,我们现在不具体分析代码,值分析uboot代码的执行流程。设置的时钟频率是FCLK是400MHz,HCLK是100MHz,PCLK是50MHz。继续往下分析代码。

#ifndef CONFIG_SKIP_RELOCATE_UBOOT	/* 没有定义此宏,执行 */
relocate:				/* relocate U-Boot to RAM	    */
	adr	r0, _start		/* r0寄存器的值等于_start当前位于的地址,也就是0   */
	ldr	r1, _TEXT_BASE		/* r1寄存器的值等于0x33F8_0000 */
	cmp     r0, r1                  /* 比较两个地址是否相等 */
	beq     clear_bss               /* 如果相等就跳转到清bss段处执行,因为到现在为止还没有完成代码的重定位,所有不相等 */
	
	ldr	r2, _armboot_start      /* r2的值等于0x33F8_0000 */
	ldr	r3, _bss_start          /* r3的值等于bss段的开始地址 */
	sub	r2, r3, r2		/* 现在r2的值等于bin文件中有效代码的长度 */
        /* 跳转拷贝代码,r0=0,r1=0x33F8_0000,r2=拷贝长度 */
	bl  CopyCode2Ram	/* r0: source, r1: dest, r2: size */

#endif	/* CONFIG_SKIP_RELOCATE_UBOOT */

clear_bss:                              /* 清除bss段 */
	ldr	r0, _bss_start		/* find start of bss segment */
	ldr	r1, _bss_end		/* stop here                 */
	mov 	r2, #0x00000000		/* clear                     */

clbss_l:str	r2, [r0]		/* 循环往bss段中写0就可以了 */
	add	r0, r0, #4
	cmp	r0, r1
	ble	clbss_l

SetLoadFlag:
	/* Set a global flag, PreLoadedONRAM  这段代码不知道干什么用的,先不管了 */
	adr	r0, _start		            /* r0 <- current position of code   */
	ldr	r1, _TEXT_BASE		            /* test if we run from flash or RAM */
	cmp     r0, r1                              /* don't reloc during debug         */
	ldr r2, =PreLoadedONRAM
	mov r3, #1
	streq r3, [r2]
        #跳转_start_armboot执行,也就是跳转到start_armboot处执行
	ldr	pc, _start_armboot		   /* 执行第二阶段代码 */

_start_armboot:	.word start_armboot

现在代码跳转到start_armboot函数执行,在"lib_arm/board.c"文件中定义。

void start_armboot (void)
{
	init_fnc_t **init_fnc_ptr;
	char *s;
	ulong size;

	/* gd的定义:#define DECLARE_GLOBAL_DATA_PTR     register volatile gd_t *gd asm ("r8")
         * 在include/asm-arm/global_data.h中定义
	*/
	
	/* Pointer is writable since we allocated a register for it 
	 * 给global data分配内存,并清空
	*/
	gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
	/* compiler optimization barrier needed for GCC >= 3.4 */
	__asm__ __volatile__("": : :"memory");

	memset ((void*)gd, 0, sizeof (gd_t));
	gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
	memset (gd->bd, 0, sizeof (bd_t));

	monitor_flash_len = _bss_start - _armboot_start;

	/* 通过函数指针,遍历init_sequence数组中的函数 */
	for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
		if ((*init_fnc_ptr)() != 0) {
			hang ();
		}
	}

	/* configure available FLASH banks */
	size = flash_init ();
	display_flash_config (size);

	/* armboot_start is defined in the board-specific linker script */
	mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);

	puts ("NAND:  ");
	nand_init();		/* go init the NAND */

	/* initialize environment */
	env_relocate ();

	/* IP Address */
	gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");

	/* MAC Address */
	{
		int i;
		ulong reg;
		char *s, *e;
		char tmp[64];

		i = getenv_r ("ethaddr", tmp, sizeof (tmp));
		s = (i > 0) ? tmp : NULL;

		for (reg = 0; reg < 6; ++reg) {
			gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
			if (s)
				s = (*e) ? e + 1 : e;
		}
	}

	devices_init ();	/* get the devices list going. */

	jumptable_init ();

	console_init_r ();	/* fully init console as a device */

	Port_Init();
	if (!PreLoadedONRAM) {
		/* enable exceptions */
		enable_interrupts ();
	    /* add by www.100ask.net */
	    usb_init();
	}

	/* Initialize from environment */
	if ((s = getenv ("loadaddr")) != NULL) {
		load_addr = simple_strtoul (s, NULL, 16);
	}


	eth_initialize(gd->bd);
	/* main_loop() can return to retry autoboot, if so just run it again. */
	for (;;) {
		main_loop ();
	}

	/* NOTREACHED - no way out of command loop except booting */
}

函数主要完成,各种外设的初始化,环境变量的初始化等,我们这里不详细分析。最后跳转到死循环"main_loop()"函数处执行代码。我们来总结下上面的这段代码主要做了哪些工作。

1.给gd指针分配空间,为以后的设置其做准备;

2.CPU、单板、环境变量、波特率、控制台和SDRAM大小相关的初始化;

3.Nor和Nand Flash识别;

4.跳转到main_loop函数去执行。

而main_loop函数主要做的工作是检查环境变量bootdelay是否倒计时为0,或是否检测到串口有输入,如果没有就获取bootcmd环境变量的值,并调用run_command函数执行命令。

uboot命令介绍:

uboot在启动内核时,也是通过Uboot命令来实现的,uboot中每个命令通过U_BOOT_CMD宏来定义的,格式如下:

U_BOOT_CMD(name,maxargs,rep,cmd,usage,help)

各项参数的意义如下:

name:命令的名字,注意,它不是一个字符串;

maxargs:最大参数的个数;

rep:命令是否可重复,可重复是指运行一个命令后,下次敲回车即可再次运行;

cmd:对应的函数指针,类型为“(struct cmd_tbl_s *, int, int, char *[])”;

usage:简短的使用说明, 这是个字符串;

help:较详细的使用说明,这是个字符串。

宏U_BOOT_CMD在include/command.h中定义,如下所示:

#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \

cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}

Struct_Section也是在include/command.h中定义,如下所示:

#define Struct_Section  __attribute__ ((unused,section (".u_boot_cmd")))

比如对于bootm命令,它如下定义:

U_BOOT_CMD(
 	bootm,	CFG_MAXARGS,	1,	do_bootm,
        "strings1",
	"strings2"
);

宏U_BOOT_CMD扩展开后如下所示:

cmd_tbl_t __u_boot_cmd_bootm __attribute__ ((unused,section (".u_boot_cmd"))) = {"bootm", CFG_MAXARGS, 1, do_bootm, "strings1", "strings2"}

对于每个使用U_BOOT_CMD宏来定义的命令,其实都是在".u_boot_cmd"段中定义一个cmd_tbl_t结构。链接脚本u-boot.lds中有如下代码:

__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;

程序中就是根据命令的名字在内存段__u_boot_cmd_start和__u_boot_cmd_end找到它的cmd_tbl_t结构体,然后调用它的函数(可以参考common/command.c中的find_cmd函数)。

内核的复制和启动,可以通过如下命令来完成,bootm从内存,ROM,Nor Flash中启动内核。它们都是先将内核映像从各种媒介中读出,存放在指定的位置,然后设置标记列表以给内核传递参数;最后跳转到内核的入口点去执行。具体的细节不在描述。

uboot启动内核:

启动命令是:

启动内核的命令是:
nand read.jffs2 0x30007FC0 kernel
bootm 0x30007FC0
从Nand中读出内核:从哪里读取,读到那里去?
从kernel分区读取内核,放到0x30007FC0地址去。

我们可以使用mtd命令查看具体分区情况:
device nand0 <nandflash0>, # parts = 4
 #: name                   size           offset    mask_flags
 0: bootloader          0x00040000      0x00000000      0
 1: params              0x00020000      0x00040000      0
 2: kernel              0x00200000      0x00060000      0
 3: root                0x0fda0000      0x00260000      0

现在来看nand read是怎么读取的?
nand read[.jffs2]  - addr off|partition size
addr:表示放到位置;
off:读取偏移量;或partition:表示分区名字;
size:表示读取的大小。
具体读取过程就不分析,大家感兴趣可以自己看看。

启动内核:
在Nand Flash上的内核是uImage;
uImage是由两部分组成,头部+真正的内核。
我们来看头部的结构:
typedef struct image_header {
uint32_t ih_magic; /* Image Header Magic Number */
uint32_t ih_hcrc; /* Image Header CRC Checksum */
uint32_t ih_time; /* Image Creation Timestamp     */
uint32_t ih_size; /* Image Data Size 数据大小 */
uint32_t ih_load; /* Data Load  Address 数据加载地址 */
uint32_t ih_ep; /* Entry Point Address 入口地址     */
uint32_t ih_dcrc; /* Image Data CRC Checksum */
uint8_t ih_os; /* Operating System */
uint8_t ih_arch; /* CPU architecture */
uint8_t ih_type; /* Image Type */
uint8_t ih_comp; /* Compression Type */
uint8_t ih_name[IH_NMLEN]; /* Image Name */
} image_header_t;

根据头部将内核移动到加载地址去。调用do_bootm_linux函数启动内核,此函数在lib_arm/armlinux.c文件中定义。
首先获取参数:
char *commandline = getenv ("bootargs");
定义一个函数指针方便跳转;
void (*theKernel)(int zero, int arch, uint params);
给函数指针复制,地址指向的内核的入口地址;
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep); 
设置参数:
setup_start_tag (bd); //参数的开始
setup_memory_tags (bd); //内存参数
setup_commandline_tag (bd, commandline); //命令行参数
setup_end_tag (bd); //参数的结束

执行跳转的:theKernel (0, bd->bi_arch_number, bd->bi_boot_params);

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目录 u-boot-1.1.6 之 cpu/arm920t/start.s分析 ........................................................................................... 2 u-boot 中.lds连接脚本文件的分析 ...................................................................................................12 分享一篇我总结的 uboot 学习笔记(转) .....................................................................................15 U-BOOT内存布局及启动过程浅析 ...................................................................................................22 u-boot 中的命令实现 .......................................................................................................................... 25 U-BOOT环境变量实现 ........................................................................................................................28 1.相关文件 ....................................................................................................................................28 2.数据结构 ....................................................................................................................................28 3.ENV 的初始化...........................................................................................................................30 3.1env_init ............................................................................................................................30 3.2 env_relocate ...................................................................................................................30 3.3*env_relocate_spec ........................................................................................................31 4. ENV 的保存 ..............................................................................................................................31 U-Boot 环境变量 ..........................................................................................................................32 u-boot 代码链接的问题 ......................................................................................................................35 ldr 和 adr 在使用标号表达式作为操作数的区别 ............................................................................40 start_armboot 浅析 ..............................................................................................................................42 1.全局数据结构的初始化 ..........................................................................................................42 2.调用通用初始化函数...............................................................................................................43 3.初始化具体设备 .......................................................................................................................44 4.初始化环境变量 .......................................................................................................................44 5.进入主循环 ...............................................................................................................................44 u-boot 编译过程 ...................................................................................................................................44 mkconfig文件的分析 .......................................................................................................................... 47 从 NAND闪存中启动 U-BOOT的设计 ..............................................................................................50 引言 ...............................................................................................................................................50 NAND闪存工作原理 ................................................................................................................... 51 从 NAND闪存启动 U-BOOT的设计思路.................................................................................. 51 具体设计 ....................................................................................................................................... 51 支持 NAND闪存的启动程序设计 ..................................................................................... 51 支持 U-BOOT命令设计 ...................................................................................................... 52 结语 ............................................................................................................................................... 53 参考文献 ....................................................................................................................................... 53 U-boot 给 kernel 传参数和 kernel 读取参数—struct tag (以及补充) ............................................ 53 1 、u-boot 给 kernel 传 RAM 参数 ........................................................................................54 2 、Kernel 读取 U-boot 传递的相关参数 .............................................................................56 3 、关于 U-boot 中的 bd 和 gd...............................................................................................59 U-BOOT源码分析及移植 ....................................................................................................................60 一、 u-boot 工程的总体结构: ..................................................................................................61 1、源代码组织 ....................................................................................................................61 2.makefile简要分析 ............................................................................................................61 3、u-boot 的通用目录是怎么做到与平台无关的?......................................................63 4、smkd2410 其余重要的文件 : ...................................................................................63 二、u-boot 的流程、主要的数据结构、内存分配 ................................................................64 1、u-boot 的启动流程: ...................................................................................................64 2、u-boot 主要的数据结构 ...............................................................................................66 3、u-boot 重定位后的内存分布: ...................................................................................68 三、u-boot 的重要细节 。 ........................................................................................................68 关于 U-boot 中命令相关的编程 : ................................................................................. 73 四、U-boot 在 ST2410 的移植,基于 NOR FLASH和 NAND FLASH启动。......................... 76 1、从 smdk2410 到 ST2410: .............................................................................................. 76 2、移植过程: .................................................................................................................... 76 3、移植要考虑的问题: ...................................................................................................77 4、SST39VF1601: .................................................................................................................77 5、我实现的 flash.c主要部分: ...................................................................................... 78 6、增加从 Nand 启动的代码 : ..................................................................................... 82 7、添加网络命令。 ............................................................................................................ 87

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值