u-boot分析

文章源于卫东山老师视频,为学习u-boot、内核移植,作者回顾所学并做笔记。内容包括上电情况,对u-boot-1.1.6解压、打补丁、编译,将编译好的u-boot.bin烧写到裸板,烧写完成后上电重启,还结合Makefile分析u-boot的功能、结构和链接方式。

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

文章来源:卫东山老师视频: 006_u-boot_内核_根文件系统(新1期_2期间的衔接)(1)\视频\第001课_U-boot分析与使用,若有侵权,请联系作者,我会删除内容。

为了学习u-boot、内核的移植,所以回过头看看已经学过的内容,顺便做个笔记

一. 上电:
PC LINUX系统

				BIOS								BootLoader
				
			引导操作系统windows					 	linux内核
				
			识别C盘,D盘						  挂接跟文件系统
				
			运行应用程序: QQ					    运行应用程序

二. 对u-boot-1.1.6进行解压,打补丁,编译:
1. 解压
tar xjf u-boot-1.1.6.tar.bz2
cd u-boot-1.1.6/

2.  打补丁
	打开补丁文件看看: 
	diff -urN u-boot-1.1.6/board/100ask24x0/100ask24x0.c u-boot-1.1.6_jz2440_20171103/board/100ask24x0/100ask24x0.c
	--- u-boot-1.1.6/board/100ask24x0/100ask24x0.c	1970-01-01 08:00:00.000000000 +0800
	+++ u-boot-1.1.6_jz2440_20171103/board/100ask24x0/100ask24x0.c	2017-11-03 14:19:59.249590991 +0800
	
	其中---代表是原来的代码,+++代表是修改后的代码,随便找一行看看:
	diff -urN u-boot-1.1.6/common/cmd_load.c u-boot-1.1.6_jz2440_20171103/common/cmd_load.c
	--- u-boot-1.1.6/common/cmd_load.c	2006-11-02 22:15:01.000000000 +0800
	+++ u-boot-1.1.6_jz2440_20171103/common/cmd_load.c	2017-11-03 14:19:59.253590991 +0800
	@@ -34,6 +34,8 @@   //这句话的意思是修改前从34行开始,共6行,修改后是从34行开始,共8行
	--- u-boot-1.1.6/common/cmd_load.c	2006-11-02 22:15:01.000000000 +0800
	+++ u-boot-1.1.6_jz2440_20171103/common/cmd_load.c	2017-11-03 14:19:59.253590991 +0800
	@@ -34,6 +34,8 @@
	 DECLARE_GLOBAL_DATA_PTR;
	 
	 #if (CONFIG_COMMANDS & CFG_CMD_LOADB)
	+/* support xmodem, www.100ask.net */
	+static ulong load_serial_xmodem (ulong offset);
	 static ulong load_serial_ymodem (ulong offset);
	 #endif
	
看看这句话: diff -urN u-boot-1.1.6/board/100ask24x0/100ask24x0.c u-boot-1.1.6_jz2440_20171103/board/100ask24x0/100ask24x0.c
意思就是补丁达到u-boot-1.1.6/board....目录下面,现在的位置是:cd u-boot-1.1.6/下面,所以打补丁应该加上一个参数p1:
	patch -p1 < ../u-boot-1.1.6_jz2440.patch

3.  配置
	u-boot的目的是为了支持各种各样的单板和芯片,所以要是想让他支持某一种单板,就需要先配置
	对于修改后的u-boot直接: make  100ask24x0_config,后面会讲如何自己配置。
	
4.  编译
	make

三. 把编译好的u-boot.bin烧写到裸板上面:
cmd
cd e:
cd E:\BaiduNetdiskDownload\002_JZ2440资料光盘_20180516(免费)(1)\资料光盘\B盘\bin\uboot
oflash u-boot.bin
后面跟着操作就行。
烧写完成后上电重启:
##### 100ask Bootloader for OpenJTAG #####
[n] Download u-boot to Nand Flash
[c] Re-scan Nor Flash
[u] Copy bootloader from nand to nor
[v] Copy bootloader from nor to nand
[k] Download Linux kernel uImage
[j] Download root_jffs2 image
[y] Download root_yaffs image
[d] Download to SDRAM & Run
[z] Download zImage into RAM
[g] Boot linux from RAM
[f] Format the Nand Flash
[s] Set the boot parameters
[b] Boot the system
[r] Reboot u-boot
[q] Quit from menu
Enter your selection:

这个信息是韦东山老师自己写的,实际上正常的u-boot是没有这些命令的,要是想查看有哪些命令,可以用help看到很多命令:
OpenJTAG> help
?       - 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
suspend - suspend the board
tftpboot- boot image via network using TFTP protocol
usbslave - get file from host(PC)
version - print monitor version
OpenJTAG>

想看这些命令的具体用法,使用? 命令
OpenJTAG> ? md
md [.b, .w, .l] address [# of objects]
	- memory display

OpenJTAG>

四. 结合Makefile分析u-boot的功能,结构,链接方式
对于u-boot来说,分析u-boot的最简单方式就是分析Makefile
1. 在Makefile中搜索100ask24x0_config,分析配置过程
100ask24x0_config : unconfig //在make这句话时候就相当于执行了下面这句话
@$(MKCONFIG) $(@:_config=) arm arm920t 100ask24x0 NULL s3c24x0
在Makefile中搜索MKCONFIG,得到下面的信息:
Line 92: MKCONFIG := (SRCTREE)/mkconfig//源文件树下面应该有一个mkconfig,也就是说u−boot下面有一个mkconfig@(SRCTREE)/mkconfig //源文件树下面应该有一个mkconfig,也就是说u-boot下面有一个mkconfig @(SRCTREE)/mkconfig//mkconfigubootmkconfig@(MKCONFIG) $(@:_config=) arm arm920t 100ask24x0 NULL s3c24x0
mkconfig 100ask24x0 arm arm920t 100ask24x0 NULL s3c24x0 //也就是说,执行配置命令的时候就相当于执行了这个脚本

	1.  分析: mkconfig     100ask24x0    arm arm920t 100ask24x0 NULL s3c24x0:
		代开mkconfig的脚本文件:
		#!/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]
		#
		# (C) 2002-2006 DENX Software Engineering, Wolfgang Denk <wd@denx.de>
		#

		#mkconfig     100ask24x0    arm arm920t 100ask24x0 NULL s3c24x0
		 $0           $1			$2	$3		$4		   $5	$6
		
		APPEND=no	# Default: Create new config file
		BOARD_NAME=""	# Name to print in make output

		//while [ $# -gt 0 ] ; do
		//	case "$1" in
		//	--) shift ; break ;;
		//	-a) shift ; APPEND=yes ;;
		//	-n) shift ; BOARD_NAME="${1%%_config}" ; shift ;;
		//	*)  break ;;
		//	esac
	//  done

		传入的参数没有 -- -a -n ,所以上面一段话可以省略,省略用//表示
		
		[ "${BOARD_NAME}" ] || BOARD_NAME="$1"  这句话的意思是如果定义了BOARD_NAME,就不执行BOARD_NAME="$1" ,否则就执行
												因为上面有一句BOARD_NAME="",所以执行完这句话后BOARD_NAME="$1",在linux中
												$1表示的是第一个参数 , BOARD_NAME="$1" = 100ask24x0

		//[ $# -lt 4 ] && exit 1  $#表示的是参数的个数,小于4退出
		//[ $# -gt 6 ] && exit 1	$#表示的是参数的个数,大于6退出,我们是6个参数,所以这两句话可以省略

		echo "Configuring for ${BOARD_NAME} board..."  然后会打印这句话,在ubuntu里面执行这句话,make 100ask24x0_config,输出信息:
		book@www.100ask.org:/work/system/u-boot-1.1.6$ make 100ask24x0_config
		Configuring for 100ask24x0 board...


		#
		# Create link to architecture specific headers
		#
		if [ "$SRCTREE" != "$OBJTREE" ] ; then
		在Makefile中搜索SRCTREE,得到的信息如下所示:
		OBJTREE		:= $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))  //这句话的意思,要是定义队了BUILD_DIR,就是BUILD_DIR,否则为CURDIR
		SRCTREE		:= $(CURDIR)
		然后搜索:BUILD_DIR
		Line 71: BUILD_DIR := $(O)  所以得出结论就是"$SRCTREE" = "$OBJTREE",所以下面一大段话可以省略
		
		
		//	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
		上面这句话的意思就是 ln -s asm-arm asm   相当于建立一个链接文件asm,指向asm-arm
		cd /work/system/u-boot-1.1.6/include/
		ls -l asm
		book@www.100ask.org:/work/system/u-boot-1.1.6/include$ ls -l asm
		lrwxrwxrwx 1 book book 7 6月  16 11:33 asm -> asm-arm
		
		为什么要这么做呢?
		book@www.100ask.org:/work/system/u-boot-1.1.6/include$ ls asm   //TAB键补全
		asm/            asm-arm/        asm-avr32/      asm-blackfin/   asm-i386/       
		asm-m68k/       asm-microblaze/ asm-mips/       asm-nios/       asm-nios2/      asm-ppc/ 
		这样做,就相当于包含头文件的时候,例如include "asm/type.h"就相当于include "asm-arm/type.h"
		这样做是为了防止将代码改来改去,直接用asm就可以了。asm是配置的时候临时生成的指向某个架构

		

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

		//if [ -z "$6" -o "$6" = "NULL" ] ; then  //如果第六个参数$6为空或者null,就执行下面这句话,但是我的$6 = s3c24x0
		//	ln -s ${LNPREFIX}arch-$3 asm-$2/arch
		//else
			ln -s ${LNPREFIX}arch-$6 asm-$2/arch  //ln -s arch-s3c24x0 asm-arm/arch   LNPREFIX没有人定义就是空的意思
			这句话的意思就是在asm-arm的目录下建立链接arch,指向arch-s3c24x0
			上面已经 cd include ,此时依旧在include目录下面
			ls -l asm-arm/arch
			book@www.100ask.org:/work/system/u-boot-1.1.6/include$ ls asm-arm/arch -l
			lrwxrwxrwx 1 book book 12 6月  16 11:33 asm-arm/arch -> arch-s3c24x0

			
			
		fi

		if [ "$2" = "arm" ] ; then
			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  建立链接
			book@www.100ask.org:/work/system/u-boot-1.1.6/include$ ls -l asm-arm/proc
			lrwxrwxrwx 1 book book 9 6月  16 11:33 asm-arm/proc -> proc-armv
		fi

		#
		# Create include file for Make   //生成配置文件
		#
		echo "ARCH   = $2" >  config.mk   // >表示新建一个文件
		echo "CPU    = $3" >> config.mk   // >>表示追加内容
		echo "BOARD  = $4" >> config.mk
		内容 : 
		 ARCH   = arm
		 CPU    = arm920t
		 BOARD  = 100ask24x0
		config.mk的目录就等于这个内容
		这个可以进去看看,依旧在include目录下面, vi config.mk
		

		//[ "$5" ] && [ "$5" != "NULL" ] && echo "VENDOR = $5" >> config.mk  //$5 = NULL

		[ "$6" ] && [ "$6" != "NULL" ] && echo "SOC    = $6" >> config.mk  //SOC = s3c24x0  >> config.mk
		
		总结下 : config.mk 的内容就是:
		 ARCH   = arm
		 CPU    = arm920t
		 BOARD  = 100ask24x0
		 SOC    = s3c24x0
		仅仅是查看内容,用cat config.mk更容易,用vi config.mk不太好用,还需要退出
		

		#
		# Create board specific header file   //创建单板相关的头文件
		#
		//if [ "$APPEND" = "yes" ]	# Append to existing config file  //搜索APPEND,APPEND=no	
		//then
		//	echo >> config.h
		//else
			> config.h		# Create new config file    //创建新的文件config.h
		fi
		echo "/* Automatically generated - do not edit */" >>config.h
		echo "#include <configs/$1.h>" >>config.h
		
		上面两句话的意思是config.h里面有下面的两句话:
		/* Automatically generated - do not edit */
		#include <configs/$1.h>
		
		以前Makefile感觉就行天书,现在看起来感觉好简单。

		exit 0
		
	上面讲解的都是配置的过程,接下来讲解的就是编译的过程 : make
2.  分析编译过程
	include $(OBJTREE)/include/config.mk
	Makefile将config.mk包含进去了,所谓的配置过程和编译过程就是这样链接的
	看看config.mk的具体内容:
		 ARCH   = arm
		 CPU    = arm920t
		 BOARD  = 100ask24x0
		 SOC    = s3c24x0
	
	
	ifndef CROSS_COMPILE
	ifeq ($(ARCH),arm)
	CROSS_COMPILE = arm-linux-
	endif
	这句话的意思就是如果arch架构是arm的话,那么交叉工具链就是arm-linux-
	
	OBJS  = cpu/$(CPU)/start.o  //这句话就不分析了,但是这个非常重要,后面会分析start.o里面的内容
	
	//库
	LIBS  = lib_generic/libgeneric.a
	LIBS += board/$(BOARDDIR)/lib$(BOARD).a   //LIBS += board/100ask24x0/lib100ask24x0.a
	LIBS += cpu/$(CPU)/lib$(CPU).a     //LIBS += cpu/arm920t/libarm920t.a 
	ifdef SOC
	LIBS += cpu/$(CPU)/$(SOC)/lib$(SOC).a
	endif
	LIBS += lib_$(ARCH)/lib$(ARCH).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
	LIBS += $(BOARDLIBS)
	
	ALL = $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)

	all:		$(ALL)

	$(obj)u-boot.hex:	$(obj)u-boot
			$(OBJCOPY) ${OBJCFLAGS} -O ihex $< $@

	$(obj)u-boot.srec:	$(obj)u-boot
			$(OBJCOPY) ${OBJCFLAGS} -O srec $< $@

	$(obj)u-boot.bin:	$(obj)u-boot    (elf,二进制格式)  然后去看看u-boot依赖于谁,可以从下面看到u-boot所依赖的
			$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@

	$(obj)u-boot.img:	$(obj)u-boot.bin
			./tools/mkimage -A $(ARCH) -T firmware -C none \
			-a $(TEXT_BASE) -e 0 \
			-n $(shell sed -n -e 's/.*U_BOOT_VERSION//p' $(VERSION_FILE) | \
				sed -e 's/"[	 ]*$$/ for $(BOARD) board"/') \
			-d $< $@

	$(obj)u-boot.dis:	$(obj)u-boot
			$(OBJDUMP) -d $< > $@

	$(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
				
	grep -nR "LDFLAGS" *
	config.mk:45:PLATFORM_LDFLAGS =
	config.mk:189:LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)
	注意一下,在上面提到了config.mk, 这里的config.mk和上面提到的一样,一个是主目录下面的,一个是include目录下面的
	grep -nR "33F800000" *
	board/100ask24x0/config.mk:25:TEXT_BASE = 0x33F80000
	单板的的SDRAM的大小是64M,从0x30000000开始,顶端空出了512K去存放u-boot的内容,也就是从33F800000开始
	所以要根据u-boot.bin的大小设置一下TEXT_BASE = 0x33F80000
	看看u-boot.bin的大小: 193 KB,所以512K是足够的
	
				
	这个一大堆东西看不懂,可以在u-boot目录下面执行make命令,在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`;\
	上面一大串不用管,然后进入某个目录
	
		cd /work/system/u-boot-1.1.6 &&       //进入某个目录
		arm-linux-ld -Bstatic -T /work/system/u-boot-1.1.6/board/100ask24x0/u-boot.lds    //这个是链接,dis是反汇编文件
		-Ttext 0x33F80000   //代码段,后面就是原材料了
		$UNDEF_SYM cpu/arm920t/start.o \   //对应上面的OBJS  = cpu/$(CPU)/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  \   //对应上面的各种库
			-Map u-boot.map -o u-boot    //最后输出u-boot文件
			
	从上面的内容可以看见脚本文件的位置: /work/system/u-boot-1.1.6/board/100ask24x0/u-boot.lds
	第一个执行的文件是: cpu/arm920t/start.o
	链接地址是 : -Ttext 0x33F80000
	可以在u-boot中搜索0x33F80000看看链接地址的来源: grep -nR "0x33F80000" *
			
			
	从u-boot-1.1.6\board\100ask24x0去看看u-boot.lds文件的内容:
	OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
	/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
	OUTPUT_ARCH(arm)
	ENTRY(_start)
	SECTIONS
	{
		. = 0x00000000;    //运行的时候位于这个位置0x33F80000 + 0x00000000

		. = ALIGN(4);   //四字节对齐
		.text      :      
		{
		  cpu/arm920t/start.o	(.text)       //首先放入start.o文件代码段
			  board/100ask24x0/boot_init.o (.text)    //再填充boot_init.o文件代码段
		  *(.text)    //最后在填入所有的文件的代码段
		}

		. = ALIGN(4);
		.rodata : { *(.rodata) }    //所有文件的只读数据段

		. = ALIGN(4);
		.data : { *(.data) }   //所有文件的数据段

		. = ALIGN(4);
		.got : { *(.got) }

		. = .;
		__u_boot_cmd_start = .;
		.u_boot_cmd : { *(.u_boot_cmd) }   //所有文件的u-boot的命令段
		__u_boot_cmd_end = .;

		. = ALIGN(4);
		__bss_start = .;    //当前地址
		.bss : { *(.bss) }   //bss段
		_end = .;
	}

	make的时候要是不指定目标的情况下就会生成第一个目标: all
	all依赖于ALL,而ALL的具体内容就是: 
	$(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)
	
3.  根据start.o开始分析u-boot的功能(第一阶段):
	以前在裸板程序的时候已经对u-boot有了一定的了解,所以大致看看就行了,u-boot其实就是一个比较复杂的单片机程序而已。
	下面是分析过程:
	先建立souce insight工程,嵌入式linux完全开发应用手册上面对底层目录有详细的解释,只需要注意一下board目录,cpu目录,
	include目录就可以了,其他的目录全部加入。source insight实际上是一个读写代码的工具,多家少加都没有什么问题,只是有些
	不需要的文件如果加入的话会干扰我们读写代码。
	以前学习裸板的时候,start.S已经了解不少了,随便看看:
	.globl _start
	_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
	reset:
		/*
		 * set the cpu to SVC32 mode   //cpu运行与SVC32模式
		 */
		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

	#if 0
		/* FCLK:HCLK:PCLK = 1:2:4 */     //初始化时钟
		/* default FCLK is 120 MHz ! */
		ldr	r0, =CLKDIVN
		mov	r1, #3
		str	r1, [r0]
	#endif
	#endif	/* CONFIG_S3C2400 || CONFIG_S3C2410 */

	#ifndef CONFIG_SKIP_LOWLEVEL_INIT
	#if 0
			/* 这些代码会使用SP,在NAND启动时会破坏片内内存的部分代码
			 * 导致NAND启动时无法使用休眠-唤醒功能
			 */
			/* 设置SP指向片内内存 */
			ldr sp, =4092
			ldr r0, =0x12345678
			str r0, [sp]
			ldr r1, [sp]
			cmp r0, r1
			ldrne sp, =0x40000000+4096
			bl clock_init
	#else
		/* 设置时钟, 使用汇编 */
	#define S3C2440_MPLL_400MHZ     ((0x5c<<12)|(0x01<<4)|(0x01))
	#define S3C2440_UPLL_48MHZ      ((0x38<<12)|(0x02<<4)|(0x02))
	#define S3C2440_CLKDIV          (0x05) // | (1<<3))    /* FCLK:HCLK:PCLK = 1:4:8, UCLK = UPLL/2 */

		ldr r1, =CLKDIVN
		mov r2, #S3C2440_CLKDIV
		str r2, [r1]

		mrc p15, 0, r1, c1, c0, 0		// read ctrl register 
		orr r1, r1, #0xc0000000 		// Asynchronous	
		mcr p15, 0, r1, c1, c0, 0		// write ctrl register

		ldr r0,=LOCKTIME
		ldr r1,=0xffffff
		str r1,[r0]
		// delay
		mov     r0, #0x200
	1:  subs    r0, r0, #1
		bne     1b

		// Configure MPLL
		ldr r0,=MPLLCON          
		ldr r1,=S3C2440_MPLL_400MHZ
		str r1,[r0]
		// delay
		mov     r0, #0x200
	1:  subs    r0, r0, #1
		bne     1b

		//Configure UPLL
		ldr     r0, =UPLLCON          
		ldr     r1, =S3C2440_UPLL_48MHZ
		str     r1, [r0]
		// delay
		mov     r0, #0x200
	1:  subs    r0, r0, #1
		bne     1b


	#endif
	#endif    

		/* 2. 根据 GSTATUS2[1]判断是复位还是唤醒 */	
		ldr r0, =GSTATUS2
		ldr r1, [r0]
		tst r1, #(1<<1)  /* r1 & (1<<1) */
		bne wake_up	

		/*
		 * we do sys-critical inits only at reboot,
		 * not when booting from ram!
		 */
	#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         */    //如果r0和r1不相等时,说明没有人初始化SDRAM
		blne	cpu_init_crit      //搜索一下cpu_init_crit函数
	#endif  
	
	
	//看看看cpu_init_crit
	#ifndef CONFIG_SKIP_LOWLEVEL_INIT
	cpu_init_crit:
		/*
		 * flush v4 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
		 */
		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
		bl	lowlevel_init   //进去看看lowlevel_init
		mov	lr, ip
		mov	pc, lr
	#endif /* CONFIG_SKIP_LOWLEVEL_INIT */
	
	
	
	
	//看看lowlevel_init
	_TEXT_BASE:
		.word	TEXT_BASE

	.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
	
	//在回到start.S继续看看内容,设置栈
	/* Set up the stack						    */
	stack_setup:
		ldr	r0, _TEXT_BASE		/* upper 128 KiB: relocated uboot   */
		sub	r0, r0, #CFG_MALLOC_LEN	/* malloc area                      */
		sub	r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo                        */

	#ifdef CONFIG_USE_IRQ
		sub	r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
	#endif
		sub	sp, r0, #12		/* leave 3 words for abort-stack    */   设置栈,栈设置好了才可以使用C函数
	
	/代码重定位/
	#ifndef CONFIG_SKIP_RELOCATE_UBOOT
	relocate:				/* relocate U-Boot to RAM	    */
		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         */
		beq     clear_bss
		
		ldr	r2, _armboot_start
		ldr	r3, _bss_start
		sub	r2, r3, r2		/* r2 <- size of armboot            */
	#if 1
		bl  CopyCode2Ram	/* r0: source, r1: dest, r2: size */
		
		
	总结: 1. 设置svc模式
		  2. 关看门狗
		  3. 屏蔽中断
		  4. 设置SDRAM
		  5. 设置栈
		  6. 初始化时钟
		  7. 代码重定位
		  8. 清BSS段,就是那些初始值为0,或者是没有初始化的静态变量和全局变量
		  9. 调用C函数: _start_armboot
		  
	上面的8个内容是u-boot的第一阶段,从第九个开始是u-boot的第二阶段。
	第二阶段例如网卡、串口、读写flash等等用C程序写的归为第二阶段,其实第一阶段也有c函数。这个c函数是韦东山老师添加进去的,
	事实上,在网上下载的u-boot函数的第一阶段基本上都是汇编语言。
	
4.  u-boot的第二阶段
	u-boot的最终目的是从flash中读出内核,然后启动内核。根据这个目的去看u-boot的代码,对于其他的就忽略掉。
	_start_armboot
		init_sequence   //这里面是一系列的初始化就不用管了
			board_init
				gd->bd->bi_arch_number = MACH_TYPE_S3C2440;   //arch编号
				 gd->bd->bi_boot_params = 0x30000100;   //启动内核参数
	
	//一些列的初始化,就是上面的init_sequence
	init_fnc_t *init_sequence[] = {
		cpu_init,		/* basic cpu dependent setup */
		board_init,		/* basic board dependent setup */
		interrupt_init,		/* set up exceptions */
		env_init,		/* initialize environment */
		init_baudrate,		/* initialze baudrate settings */
		serial_init,		/* serial communications setup */
		console_init_f,		/* stage 1 init of console */
		display_banner,		/* say that we are here */
	#if defined(CONFIG_DISPLAY_CPUINFO)
		print_cpuinfo,		/* display cpu info (and speed) */
	#endif
	#if defined(CONFIG_DISPLAY_BOARDINFO)
		checkboard,		/* display board info */
	#endif
		dram_init,		/* configure available RAM banks */
		display_dram_config,
		NULL,
	};			
	
	要是想读取内核以及开发的方便,u-boot需要具备两种能力: 读取flash和写flash
	对于NOR flash要是想写需要先识别出是哪种类型的flash,NAND Flash也是一样,需要做一些初始化,以及识别是什么类型。
	
	#ifndef CFG_NO_FLASH
		/* configure available FLASH banks */
		size = flash_init ();   //对NOR flash的初始化
		display_flash_config (size);
	#endif /* CFG_NO_FLASH */

	u-boot是一个单片机程序,分配内存、释放内存都需要自己去实现:
	mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
	malloc是从内存中分配出一块空间,malloc称之为堆,对应192K那块区域,在这块空间上实现堆的释放和分配
	
	nand_init();  //初始化NAND FLASH
	
	env_relocate ();   //环境变量初始化,在u-boot界面输入print就可以看见一堆的数据,那个就是环境变量,前面是环境变量,等号后面
	的是环境变量的值
	环境变量的值来源于两种:默认的(代码中写死的)、可以修改的存在于flash中的
	
		/* MAC Address */   这个不用管
		
	devices_init ();	/* get the devices list going. */    //这个也不用管
	
		if (!PreLoadedONRAM) {      //调试器相关的代码
			/* enable exceptions */
			enable_interrupts ();
			/* add by www.100ask.net */
			usb_init();      
		}
		
	/* Perform network card initialisation if necessary */   网卡相关的代码
	
	for (;;) {         //进入循环
		main_loop ();
	}
			  
	一直讲解到这里都还没有看到u-boot的最终的目的: 启动内核。进去看看main_loop()
	start_armboot
		main_loop
			setenv ("bootcount", bcs_set);
			s = getenv ("bootdelay");
			s = getenv ("bootcmd");    //启动命令,这个参数非常重要
			
		if (bootdelay >= 0 && s && !abortboot (bootdelay)) {    //倒数计时
	# ifdef CONFIG_AUTOBOOT_KEYED
			int prev = disable_ctrlc(1);	/* disable Control C checking */
	# endif

	# ifndef CFG_HUSH_PARSER
			{
				printf("Booting Linux ...\n");            
				run_command (s, 0);
			}
	# else
			parse_string_outer(s, FLAG_PARSE_SEMICOLON |
						FLAG_EXIT_FROM_LOOP);
	# endif

	# ifdef CONFIG_AUTOBOOT_KEYED
			disable_ctrlc(prev);	/* restore Control C checking */
	# endif
		}
		
	bootcmd=nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0
	这句话包含了两个命令:nand read.jffs2 0x30007FC0 kernel   //用命令nand read.jffs2,读到0x30007FC0,从kernel读

	main_loop是一个循环函数,在u-boot窗口一直等待我们输入命令,输入什么命令就解析什么命令
	
5.  u-boot命令
	后面老子听不懂了..................
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值