01_u-boot分析

u-boot分析

编译体验

windows和嵌入式单板 上电的对比

上电上电
PC嵌入式单板
BIOSbootloader
引导操作系统windows引导linux内核
识别C、D盘挂接根文件系统
运行应用程序QQ、MSN运行应用程序

bootloader的最终目的:启动内核

尝试编译bootloadr

1、搭建ubuntu16.04-64.bit开发环境

2、从这里下载交叉编译工具 http://www.arm9.net/download.asp arm-linux-gcc-4.4.3-20100728.tar.gz

# 解压
tar xvf arm-linux-gcc-4.4.3-20100728.tar.gz
# 将交叉编译工具路径配置到到环境变量中
export PATH="/home/showme/workspace/tools/opt/FriendlyARM/toolschain/4.4.3/bin:$PATH"

3、从附件中获取文件,并放入虚拟机中

u-boot-1.1.6.tar.bz2 u-boot-1.1.6_jz2440.patch 两个文件放在同一目录下

# 解压
tar xvf u-boot-1.1.6.tar.bz2
# 打补丁
cd u-boot-1.1.6
patch -p1 < ../u-boot-1.1.6_jz2440.patch
# 配置 (uboot的目的是支持很多单板,在编译之前需要告诉u-boot当前需要支持哪种板子)
make 100ask24x0_config
# 编译
mkae

4、uboot.bin写入到jz2440开发板中
在这里插入图片描述

5、使用自己编译的的u-boot

##### 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: q    # 退出韦东山写的命令,进入到uboot的命令界面
OpenJTAG> <INTERRUPT>
OpenJTAG>

u-boot的简单使用

# 输入help可以查看所有的命令

# 增长uboot启动倒计时时间
# 保存设置
# 重新启动

OpenJTAG> set bootdelay 10
OpenJTAG> save
Saving Environment to NAND...
Erasing Nand...Writing to Nand... done
OpenJTAG> reset

在这里插入图片描述

u-boot最终目的-启动内核,那uboot大概会做哪些事:

  • 从Flash读出内核-放到SDRAM
  • 启动内核

从上面可知,uboot需要做哪些(现在还不全):

1、需要读出内核:u-boot能操作Flash

2、放到SDRAM:初始化SDRAM

​ 当然初始化SDRAM还不够(需要对更多的硬件进行初始化):

​ 需要初始化时钟(才上电的时候,运行频率太低,需要提高运行频率),

​ 初始化串口(观察运行信息)

​ 关闭看门狗

3、启动内核

Makefile结构分析

在分析一个源码项目时,可以通过阅读README.txt,Makefile文件,知晓项目的组织结构

当前编译uboot经过了两个步骤:

  • 配置 make 100ask24x0_config
  • 编译 make

配置过程

查看源码根目录下的Makefile文件:搜索 100ask24x0_config

# Makefile中的内容
100ask24x0_config	:	unconfig
	@$(MKCONFIG) $(@:_config=) arm arm920t 100ask24x0 NULL s3c24x0

在Makefile中定义MKCONFIG

MKCONFIG	:= $(SRCTREE)/mkconfig

最终执行 make 100ask24x0_config,实际执行为

# $0         $1      $2   $3      $4        $5    $6
mkconfig 100ask24x0 arm arm920t 100ask24x0 NULL s3c24x0

查看源码根目录下的mkconfig文件 : (看下面TODO 之后的注释就行)

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

# TODO 这个while条件不成立,跳过
while [ $# -gt 0 ] ; do
	case "$1" in
	--) shift ; break ;;
	-a) shift ; APPEND=yes ;;
	-n) shift ; BOARD_NAME="${1%%_config}" ; shift ;;
	*)  break ;;
	esac
done

# TODO BOARD_NAME 被赋值为 100ask24x0
[ "${BOARD_NAME}" ] || BOARD_NAME="$1"

# TODO 参数大于4小于等于6,下面两行不成立
[ $# -lt 4 ] && exit 1
[ $# -gt 6 ] && exit 1

echo "Configuring for ${BOARD_NAME} board..."

#
# Create link to architecture specific headers
#
# TODO if 条件不成立,进入else中
# TODO 进入到include目录,删除原先的asm文件夹,然后建立arm软链接文件夹,指向asm-arm
# TODO include下有asm-i386、asm-mips、asm-arm等架构文件,一般开发中引用头文件
# TODO <asm-i386/type.h>或者是<asm-mips/types> 这种写法
# TODO 通过使用  ln -s asm-$2 asm
# TODO 编写代码时,只需要要 <asm/type.h>,让上一层忽略底层的架构差异
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

rm -f asm-$2/arch

if [ -z "$6" -o "$6" = "NULL" ] ; then
	ln -s ${LNPREFIX}arch-$3 asm-$2/arch
else
# TODO ln -s arch-s3c24x0 arm-arm/arch
	ln -s ${LNPREFIX}arch-$6 asm-$2/arch
fi

if [ "$2" = "arm" ] ; then
	rm -f asm-$2/proc
	# TODO ln -s proc-armv arm-arm/arch
	ln -s ${LNPREFIX}proc-armv asm-$2/proc
fi

#
# Create include file for Make
#
# TODO 在include下新建config.mk文件
# TODO 写入 内容
# ARCH   = arm
# CPU    = arm920t
# BOARD  = 100ask24x0
# SOC    = s3c24x0
echo "ARCH   = $2" >  config.mk
echo "CPU    = $3" >> config.mk
echo "BOARD  = $4" >> config.mk

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

[ "$6" ] && [ "$6" != "NULL" ] && echo "SOC    = $6" >> config.mk

#
# Create board specific header file
#TODO 创建一个单板相关的头文件 config.h
#TODO 写入内容 "#include <configs/100ask24x0.h>"
#
if [ "$APPEND" = "yes" ]	# Append to existing config file
then
	echo >> config.h
else
	> config.h		# Create new config file
fi
echo "/* Automatically generated - do not edit */" >>config.h
echo "#include <configs/$1.h>" >>config.h

exit 0

配置过程的整体目的如下

  • 指定开发板名称BOARD_NAME为100ask24x0
  • 创建平台/开发板相关的头文件的链接
  • 创建顶层Makefile包含的文件include/config.mk,其中是板子的架构,CPU类型,板名等信息
  • 创建头文件 include/config.h 包含开发板相关 的头文件

编译过程

导入了配置生成的配置文件

include $(OBJTREE)/include/config.mk
# U-Boot objects....order is important (i.e. start must be first)
OBJS  = cpu/$(CPU)/start.o
ifeq ($(CPU),i386)
OBJS += cpu/$(CPU)/start16.o
OBJS += cpu/$(CPU)/reset.o
endif
ifeq ($(CPU),ppc4xx)
OBJS += cpu/$(CPU)/resetvec.o
endif
ifeq ($(CPU),mpc83xx)
OBJS += cpu/$(CPU)/resetvec.o
endif
ifeq ($(CPU),mpc85xx)
OBJS += cpu/$(CPU)/resetvec.o
endif
ifeq ($(CPU),mpc86xx)
OBJS += cpu/$(CPU)/resetvec.o
endif
ifeq ($(CPU),bf533)
OBJS += cpu/$(CPU)/start1.o	cpu/$(CPU)/interrupt.o	cpu/$(CPU)/cache.o
OBJS += cpu/$(CPU)/cplbhdlr.o	cpu/$(CPU)/cplbmgr.o	cpu/$(CPU)/flush.o
endif

OBJS := $(addprefix $(obj),$(OBJS))

LIBS  = lib_generic/libgeneric.a
LIBS += board/$(BOARDDIR)/lib$(BOARD).a
LIBS += cpu/$(CPU)/lib$(CPU).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)

OBJS,LIBS 把各个目录下的文件打包为一个库,生成uboot的原材料库

编译命令:make,在make之后没有指定其他的命令,那在Makefile文件中,就会执行第一个命令

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
		$(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

生成u-boot.bin,命令展开为

make[1]: Leaving directory '/mnt/hgfs/weidongshan/001_002/u_boot_jz2440/u-boot-1.1.6/common'
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 /mnt/hgfs/weidongshan/001_002/u_boot_jz2440/u-boot-1.1.6 && arm-linux-ld -Bstatic -T /mnt/hgfs/weidongshan/001_002/u_boot_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  \
		-Map u-boot.map -o u-boot

从上可以看到,通过链接脚本,将各种原材料库生成为uboot

100ask24x0/u-boot.lds -Ttext 0x33F80000

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
	. = 0x00000000;

	. = ALIGN(4);
	.text      :
	{
	  cpu/arm920t/start.o	(.text)
          board/100ask24x0/boot_init.o (.text)
	  *(.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_cmd_end = .;

	. = ALIGN(4);
	__bss_start = .;
	.bss : { *(.bss) }
	_end = .;
}

链接脚本中的.0x00000000会加上命令中的-Ttext 0x33F80000 ,最终会从0x33F80000地址开始往后排布

最开始排列放置的是

1、cpu/arm920t/start.o (.text) 这个比较特别、board/100ask24x0/boot_init.o (.text)这个比较特别 和其他文件的.text段

2、所有文件的.rodata、.data、.got

3、.u_boot_cmd 段 这个比较特别

4、.bss段

uboot从内存0x33F80000开始运行,最开始运行的文件是cpu/arm920t/start.o

这个0x33F80000是在 board/100ask24x0/config.mk定义,写死在文件中的。
在这里插入图片描述

js2440中SDRAM大小为64M,地址从0x30000000,uboot自身放在64M的最上方的512K处。当uboot的大小超过512K,可以减小0x33F80000 地址,从而放置更大的uboot

源码第1阶段

在第一期中,写单片机程序时,一般的操作

  • 关看门狗
  • 初始化时钟
  • 初始化SDRAM
  • 当程序稍微大一点时,需要把程序从Nand拷贝到SDRAD
  • 设置cp,指向模块内存,因为用到了C程序

uboot本身也是复杂的单片机程序,如上的操作,uboot也需要做:硬件相关的初始化

下面从cpu/arm920t/start.S开始分析uboot的功能

第一条指令,跳转到reset

.globl _start
_start:	b       reset
	add	pc, pc, #0x0c000000
	add	pc, pc, #0x0c000000
	add	pc, pc, #0x0c000000
	add	pc, pc, #0x0c000000
	add	pc, pc, #0x0c000000
	add	pc, pc, #0x0c000000
	add	pc, pc, #0x0c000000

	.balignl 16,0xdeadbeef

1、设置CPU为SVC 管理模式

/*
	 * set the cpu to SVC32 mode
	 */
	mrs	r0,cpsr
	bic	r0,r0,#0x1f
	orr	r0,r0,#0x13
	msr	cpsr,r0

2、关闭看门狗

	ldr     r0, =pWTCON
	mov     r1, #0x0
	str     r1, [r0]

3、屏蔽所有的中断

mov	r1, #0xffffffff
	ldr	r0, =INTMSK
	str	r1, [r0]

4、做一些硬件初始化,主要是SDRAM的初始化

单片机一上电,nand前4K的内容复制到片内内存中,地址从0开始。

_start = 0

_TEXT_BASE = 0x33F80000

当这两个地址不相等,说明当前还是运行在片内内存中(还不是uboot将自身拷贝到SDRAM中,在SDRAM中运行),SDRAM相关的内容还没有初始化

#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
#endif

cpu_init_crit : 关闭Cache,MMU,初始化SDRAM

5、之后设置栈

	/* 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    */    

在这里插入图片描述

6、初始化时钟

7、重定位代码 : 从Flash读到SDRAM

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 */
#else
	add	r2, r0, r2		/* r2 <- source end address         */

copy_loop:
	ldmia	r0!, {r3-r10}		/* copy from source address [r0]    */
	stmia	r1!, {r3-r10}		/* copy to   target address [r1]    */
	cmp	r0, r2			/* until source end addreee [r2]    */
	ble	copy_loop

从Flash拷贝代码到SDRAM

int CopyCode2Ram(unsigned long start_addr, unsigned char *buf, int size)
{
    unsigned int *pdwDest;
    unsigned int *pdwSrc;
    int i;

    if (bBootFrmNORFlash())
    {
        pdwDest = (unsigned int *)buf;
        pdwSrc  = (unsigned int *)start_addr;
        /* 从 NOR Flash启动 */
        for (i = 0; i < size / 4; i++)
        {
            pdwDest[i] = pdwSrc[i];
        }
        return 0;
    }
    else
    {
        /* 初始化NAND Flash */
		nand_init_ll();
        /* 从 NAND Flash启动 */
        nand_read_ll_lp(buf, start_addr, (size + NAND_BLOCK_MASK_LP)&~(NAND_BLOCK_MASK_LP));
		return 0;
    }
}

判断是从nand flash还是从nor启动,如下判断方法比较巧妙

int bBootFrmNORFlash(void)
{
    volatile unsigned int *pdw = (volatile unsigned int *)0;
    unsigned int dwVal;
    
    /*
     * 无论是从NOR Flash还是从NAND Flash启动,
     * 地址0处为指令"b	Reset", 机器码为0xEA00000B,
     * 对于从NAND Flash启动的情况,其开始4KB的代码会复制到CPU内部4K内存中,
     * 对于从NOR Flash启动的情况,NOR Flash的开始地址即为0。
     * 对于NOR Flash,必须通过一定的命令序列才能写数据,
     * 所以可以根据这点差别来分辨是从NAND Flash还是NOR Flash启动:
     * 向地址0写入一个数据,然后读出来,如果没有改变的话就是NOR Flash
     */

    dwVal = *pdw;       
    *pdw = 0x12345678;
    if (*pdw != 0x12345678)
    {
        return 1;
    }
    else
    {
        *pdw = dwVal;
        return 0;
    }
}

8、清除bss

clear_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]		/* clear loop...                    */
	add	r0, r0, #4
	cmp	r0, r1
	ble	clbss_l

9、调用C函数

ldr	pc, _start_armboot

源码第2阶段

uboot的目标:启动内核

  • 从Flash读出内核
  • 启动内核

如下分析,仅分析上面的目标,其余忽略

_start_armboot 在lib_arm/board.c中实现

/* Pointer is writable since we allocated a register for it */
	gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));

gd结构指针指向的位置如下如下标红所示,该内存存放启动内核相关操作的参数
在这里插入图片描述

之后通过函数指针,对硬件进行初始化

for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
		if ((*init_fnc_ptr)() != 0) {
			hang ();
		}
	}

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,
};

查看 board_init 初始化,文件在 board/100ask24x0/100ask24x0.c

设置了 arch_number 架构名(机器ID),boot_params 内核启动参数存放的位置

    /* support both of S3C2410 and S3C2440, by www.100ask.net */
    if (isS3C2410)
    {
        /* arch number of SMDK2410-Board */
        gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;
    }
    else
    {
        /* arch number of SMDK2440-Board */
        gd->bd->bi_arch_number = MACH_TYPE_S3C2440;
    }

    /* adress of boot parameters */
    gd->bd->bi_boot_params = 0x30000100;

回到 lib_arm/board.c 的 start_armboot函数中

进行了nor flash的初始化

	size = flash_init ();
	display_flash_config (size);

uboot自己实现了堆

	mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);

位置如下

在这里插入图片描述

之后对nand flash 进行初始化

nand_init();

到现在 nand flash、nor flash、sdram都初始化完成,uboot有能力,将flash上的内核读放到SDRAM中


接着,初始化环境变量

env_relocate ();

环境变量,就是uboot命令行中执行print命令输出的内容

bootargs=noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0
bootcmd=nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0
baudrate=115200
ethaddr=08:00:3e:26:0a:5b
ipaddr=192.168.7.17
serverip=192.168.7.11
netmask=255.255.255.0
mtdids=nand0=nandflash0
mtdparts=mtdparts=nandflash0:256k@0(bootloader),128k(params),2m(kernel),-(root)
bootdelay=10
stdin=serial
stdout=serial
stderr=serial
partition=nand0,0
mtddevnum=0
mtddevname=bootloader

这些环境变量从哪里来:

  • 代码中写死的
  • Flash保存的

statr_armboot经过一系列的初始化 flash_init,nand_init,进入main_loop函数,该函数在common/main.c 中

启动内核

s = getenv ("bootcmd");
run_command (s, 0);
bootcmd=nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0

uboot从kernel分区读取内核到0x30007FC0 , 启动内核bootm 0x30007FC0。

到现在为止知道:内核启动就是通过 run_command 函数,和 bootcmd 字符串中的命令进行实现的

u-boot命令实现

在u-boot的命令终端上,输入内容;u-boot解析字符串,找到对应的函数,执行。

在 u-boot存在命令的结构体,包含了:字符串命令,该命令的函数指针

查看run_command函数实现,在common/main.c文件中

命令大致流程:

1、解析字符串

2、通过字符串找到命令结构体 cmd_tbl_t。在uboot的链接脚本中存在一个__u_boot_cmd段,在这个段中查找结构体(所有的命令都是存放在这里)

3、通过结构体,获得执行该命令的函数指针

4、执行函数,同时附加上参数

cmd_tbl_t *cmdtp
cmdtp = find_cmd(argv[0]))
cmdtp->cmd == do_bootd
if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {

添加命令

流程2中,命令如何实现 include/command.h

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

#ifdef  CFG_LONGHELP

#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}

#else	/* no long help info */

#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage}

自己添加一个命令 hello : 只需要添加一个命令实现的文件,修改common/Makefile文件

1、common/cmd_hello.c

#include <common.h>
#include <watchdog.h>
#include <command.h>
#include <image.h>
#include <malloc.h>
#include <zlib.h>
#include <bzlib.h>
#include <environment.h>
#include <asm/byteorder.h>


int do_hello (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]){
    printf("hello world!\r\n");
    return 0;
}

U_BOOT_CMD(
        hello,	CFG_MAXARGS,	1,	do_hello,
        "hello  - just text",
        "hello , long help"
        );

2、common/Makefile : 添加cmd_hello.o

COBJS	= main.o ACEX1K.o altera.o bedbug.o circbuf.o \
	  cmd_ace.o cmd_autoscript.o \
	  cmd_bdinfo.o cmd_bedbug.o cmd_bmp.o cmd_boot.o cmd_bootm.o \
	  cmd_cache.o cmd_console.o \
	  cmd_date.o cmd_dcr.o cmd_diag.o cmd_display.o cmd_doc.o cmd_dtt.o \
	  cmd_eeprom.o cmd_elf.o cmd_ext2.o \
	  cmd_fat.o cmd_fdc.o cmd_fdos.o cmd_flash.o cmd_fpga.o \
	  cmd_i2c.o cmd_ide.o cmd_immap.o cmd_itest.o cmd_jffs2.o \
	  cmd_load.o cmd_log.o \
	  cmd_mem.o cmd_mii.o cmd_misc.o cmd_mmc.o \
	  cmd_nand.o cmd_net.o cmd_nvedit.o \
	  cmd_pci.o cmd_pcmcia.o cmd_portio.o cmd_hello.o \
	  cmd_reginfo.o cmd_reiser.o cmd_scsi.o cmd_spi.o cmd_universe.o \
	  cmd_usb.o cmd_usbslave.o cmd_menu.o cmd_vfd.o \
	  command.o console.o devices.o dlmalloc.o docecc.o \
	  environment.o env_common.o \
	  env_nand.o env_dataflash.o env_flash.o env_eeprom.o \
	  env_nvram.o env_nowhere.o \
	  exports.o \
	  flash.o fpga.o ft_build.o \
	  hush.o kgdb.o lcd.o lists.o lynxkdi.o \
	  memsize.o miiphybb.o miiphyutil.o \
	  s_record.o serial.o soft_i2c.o soft_spi.o spartan2.o spartan3.o \
	  usb.o usb_kbd.o usb_storage.o \
	  virtex2.o xilinx.o crc16.o xyzModem.o cmd_mac.o cmd_suspend.o

编译烧写,查看结果

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'
hello  - just texthelp    - print online help


OpenJTAG> hello
hello world!
OpenJTAG>

u-boot启动内核

u-boot启动内核核心

nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0

对于Flash来说,没有分区表,这个kernel分区是写死在代码中的 include/configs/100ask24x0.h

#define MTDIDS_DEFAULT "nand0=nandflash0"
#define MTDPARTS_DEFAULT "mtdparts=nandflash0:256k@0(bootloader)," \
                            "128k(params)," \
                            "2m(kernel)," \
                            "-(root)"

通过mtd命令查看分区的信息

OpenJTAG> 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

active partition: nand0,0 - (bootloader) 0x00040000 @ 0x00000000

defaults:
mtdids  : nand0=nandflash0
mtdparts: mtdparts=nandflash0:256k@0(bootloader),128k(params),2m(kernel),-(root)

第一条命令

nand read.jffs2 0x30007FC0 kernel

# 相当于
nand read.jffs2 0x30007FC0 0x00060000 0x00200000

从nand中0x00060000的位置,读取0x00200000这么大的内容,到内存0x30007FC0处

第二条命令

bootm 0x30007FC0

从 common/cmd_bootm.c 的 do_bootm 函数开始分析

在Flash上,内核的格式为zImage : image_header (大小64字节)+ 内核

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;

ih_load : 加载地址,即内核镜像整体要放置的内存空间位置 0x30007FC0

ih_ep : 入口地址 :即从内核镜像中开始执行的地址 0x30008000

在这里插入图片描述

调整内核在内存中的位置

do_bootm 一上来会根据 头部信息,将内核移动到合适的地方,例子:

nand read.jffs2 地址A kernel; bootm 地址A

可以从Flash将kernel分区中的内核读到内存的任意位置(只要这个位置没有破坏内存的其他结构),do_bootm读取image_header的加载地址,再将内核移动到加载地址指定的位置。

在我们这个linux 内核中ih_load就是 0x30007FC0,且启动命令就是 bootm 0x30007FC0 ,可以不要做额外的动作,移动内核,节省了时间

do_bootm_linux启动内核

1、uboot告诉内核一些启动参数

在某个地址按照某种格式,保存数据,

这个格式称为 TAG

地址在JZ2440是0x30000100 : 就是 在lib_arm/board.c中_start_armboot函数实现 中 gd指针指向的位置(源码第2阶段的时候,有讲)

setup_start_tag (bd_t *bd);
setup_memory_tags (bd_t *bd);
setup_commandline_tag (bd_t *bd, char *commandline);
		// bootargs=noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0
		//                          根文件系统     第一个应用程序     内核打印信息从哪里打印:串口
setup_end_tag (bd_t *bd);

这些TAG,具体的就不看了,反正就是放参数的

2、跳转到内核启动地址

image_header_t *hdr = &header;
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep); //获取 内核启动地址
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值