u-boot分析
编译体验
windows和嵌入式单板 上电的对比
上电 | 上电 |
---|---|
PC | 嵌入式单板 |
BIOS | bootloader |
引导操作系统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);