Uboot小结

	最近在看U-boot的源码,看得是昏天暗地,只为一心想知道命令行输入nand write.uboot 这命令后,程序内部的走向,下面是我所了解的,还有许多不明白的地方,希望看官们指点!

1、顶层Makefile文件

为了看懂Makefile,自己找了一些基础教程,这个(http://wiki.ubuntu.org.cn/%E8%B7%9F%E6%88%91%E4%B8%80%E8%B5%B7%E5%86%99Makefile:%E4%B9%A6%E5%86%99%E8%A7%84%E5%88%99)介绍的比较多,但也不是特别全面。

贴上Makefile 里的内容:
forlinx_nand_ram256_config :  unconfig
@$(MKCONFIG) smdk6410 arm s3c64xx smdk6410 samsung s3c6410 NAND ram256

从最开始的配置,便是配置哪一块开发板,我用的是6410这个,在linux的Uboot目录下,敲入命令行"make  forlinx_nand_ram256_config "

此时,make就会以forlinx_nand_ram256_config 做为目标,然后寻找依赖,我们看到依赖是unconfig,在Makefile里找到unconfig的内容:

unconfig:
@rm -f $(obj)include/config.h $(obj)include/config.mk \
$(obj)board/*/config.tmp $(obj)board/*/*/config.tmp

可以看到unconfig里只是做了删除操作——rm ,变量$(obj)此时是空的。通过这个unconfig,我们也可以猜想到

@$(MKCONFIG) smdk6410 arm s3c64xx smdk6410 samsung s3c6410 NAND ram256

这条命令多半是跟删除的文件有莫大的联系。@$(MKCONFIG) 是执行MKCONFIG这个shell脚本,而后面的smdk6410 arm s3c64xx smdk6410 samsung s3c6410 NAND ram256将作为脚本参数传入!

找到uboot目录下面的MKCONFIG,它大概做了①Create link to architecture specific headers  创建指定头文件的结构链接,大概是整改了uboot/include/asm 里的文件

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 #the rm is the past 
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
ln -s ${LNPREFIX}arch-$6 asm-$2/arch
fi


后面的代码也类似,都是根据传入的参数进行创建板级相关的文件,

echo "ARCH   = $2" >  config.mk #cd ./include
echo "CPU    = $3" >> config.mk
echo "BOARD  = $4" >> config.mk

#
# Create board specific header file
#
if [ "$APPEND" = "yes" ] # Append to existing config file
then
echo >> config.h
else
> config.h # Create new config file #这里>表示追加,>>表示清空原文件内容,然后写数据。
fi

  echo "TEXT_BASE = 0xC7E00000" >> ../board/samsung/smdk6410/config.mk#这里生成了config.mk的文件,里面放置了TEXT_BASE的地址,即Uboot代码段的基地址。


2、启动Start.S(参考:http://www.crifan.com/files/doc/docbook/uboot_starts_analysis/release/htmls/index.html)

启动代码主要进行了映射地址的设置

bl lowlevel_init/* go setup pll,mux,memory */

跳转到 lowlevel_init 执行时钟、uart、nand、ddr等初始化,这些初始化的所需参数都是有数据手册查阅得来的。

接着Start.S要开始搬运程序到内存。注意,第一条指令的执行是硬件自动将程序从nand里拷贝到SDRAM中去的,这个叫做steppingstone,拷贝的程序大小为8K,而uboot显然大于8K,故程序需要在前面初始化完ddr后,将代码从nandflash里复制到ddr中,这里便是复制到ddr中的0xce700000去,即text_base。另外,在u_boot前面8K的程序,所用都是相对地址的代码,用的是BL之类的指令,是在当前地址处加上偏移值而得到的,若用mov pc ,#立即数 这个代码,这是绝对地址代码,此时要跳转的那个位置还没有复制代码过去,故程序就飞了!

接下来设置栈指针,为后面的C运行搭建环境

stack_setup:
#ifdef CONFIG_MEMORY_UPPER_CODE
ldr sp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE - 0xc)
#else
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
subsp, r0, #12/* leave 3 words for abort-stack    */
#endif

初始化_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
ldr pc, _start_armboot

其中_bss_start 与__bss_end 是从链接脚本里导入的

extern ulong _bss_start; /* code + data end == BSS start */
extern ulong _bss_end; /* BSS end */

用一全局变量声明即可用。

最后跳转到_start_armboot执行C程序

3、环境变量名与值

这个是纠结我最久的一个问题,如果你愿意一层一层一层一层的剥开它的心,你会发现,它还有一层。。。直接泪奔。还是总结下所了解到的吧,虽然还不是全面了解了:

char *getenv (char *name)

首先是char *getenv (char *name)这个函数,输入环境变量名,返回相应环境变量的值。该函数里调用了用外层for 循环一次次调用env_get_char()期望获得环境变量列表每一行的首地址,(static uchar default_environment[] 是默认环境变量列表,里面全是字符串,用‘\0’显示的隔开,但每一个环境变量的长度不一致) 又调用了static int envmatch (uchar *s1, int i2) 进行环境变量的match。若匹配到了,则返回变量名“=”号后一个字符串的地址,调用env_get_addr()最终得到返回的值。一层一层找下去,发现关键在于下面这个函数

uchar env_get_char_spec (int index)
{
return ( *((uchar *)(gd->env_addr + index)) );
}

gd是个全局变量,有这个宏定义定义的DECLARE_GLOBAL_DATA_PTR;

#define DECLARE_GLOBAL_DATA_PTR     register volatile gd_t *gd asm ("r8")

而gd->env_addr 经过层层指向,最终还是指到了default_environment[] 这个命令列表。全局变量的指定可能会在某些初始化函数里面,注意!

贴上两个default_environment的内容。

static uchar default_environment[] = {
#if defined(CONFIG_BOOTARGS)
"bootargs=" CONFIG_BOOTARGS "\0"
#endif
#if defined(CONFIG_BOOTCOMMAND)
"bootcmd=" CONFIG_BOOTCOMMAND "\0"
#endif}

4、命令的搜索与执行

main_loop()是主循环,人机交互都在这里了。

s = getenv ("bootcmd");这里获取了bootcmd的值#define CONFIG_BOOTCOMMAND"nand read 0xc0008000 0x200000 0x500000;bootm 0xc0008000"

parse_string_outer(s, FLAG_PARSE_SEMICOLON |   FLAG_EXIT_FROM_LOOP);

由static int parse_string_outer(const char *s, int flag)进行分析执行。调用setup_string_in_str(&input, s); 将s 放进input里,调用parse_stream_outer(&input, flag);进行下一步的分析。过程很复杂,还没看得很透彻,但是大概的过程是将命令行解析出参数及命令。(cmdtp = find_cmd(child->argv[i]))查找命令, 然后调用rcode = (cmdtp->cmd)来执行。这里再讲下查找的方式。

命令是放在common文件夹下的,每一个设备的命令对应一个cmd.c文件例如cmd_nand.c之类,这就是nand里的命令

U_BOOT_CMD(
        nand, 5,1,do_nand,
        "nand    - legacy NAND sub-system\n",
        "info  - show available NAND devices\n"
        "nand device [dev] - show or set current device\n"
        "nand read[.jffs2[s]]  addr off size\n"
        "nand write[.jffs2] addr off size - read/write `size' bytes starting\n"
        "    at offset `off' to/from memory address `addr'\n"
        "nand erase [clean] [off size] - erase `size' bytes from\n"
        "    offset `off' (entire device if not specified)\n"
        "nand bad - show bad blocks\n"
        "nand read.oob addr off size - read out-of-band data\n"
        "nand write.oob addr off size - read out-of-band data\n"
        "nand beep--burn ok to beep\n"
        "nand led---burn ok to led light on and off \n"
        );

由common.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} 

这里的宏定义  "##"是链接字符串, "#"是将变量名变成字符串,nand中的U_BOOT_CMD()经如处理后会变成

cmd_tbl_t __u_boot_cmd_nand(“nand”,5, 1,do_nand

        "nand    - legacy NAND sub-system\n",
        "info  - show available NAND devices\n"
        "nand device [dev] - show or set current device\n"
        "nand read[.jffs2[s]]  addr off size\n"
        "nand write[.jffs2] addr off size - read/write `size' bytes starting\n"
        "    at offset `off' to/from memory address `addr'\n"
        "nand erase [clean] [off size] - erase `size' bytes from\n"
        "    offset `off' (entire device if not specified)\n"
        "nand bad - show bad blocks\n"
        "nand read.oob addr off size - read out-of-band data\n"
        "nand write.oob addr off size - read out-of-band data\n"
        "nand beep--burn ok to beep\n"
        "nand led---burn ok to led light on and off \n")

其中do_nand 就是对应的处理的函数。在那个宏定义里还有一个  Struct_Section ,这里是一个宏定义,原型是

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

这里用了attribute的机制(attribute在预编译是作用,用来提醒编译器的,这是我的理解),unused 这里不是很懂,重要的是setction(".u_boot_cmd")将这些cmd_tbl_t  u_boot_cmd_##name 的变量放在.u_boot_cmd段中,只是为了方便管理,然后链接脚本了指定了.u_boot_cmd段的地址,于是就可以像之前_bss段那样导入,然后进行寻址了。于是find_cmd 就是这么干的!

贴下find_cmd的部分代码:

cmdtp = &__u_boot_cmd_start;
    cmdtp != &__u_boot_cmd_end;
    cmdtp++) {
if (strncmp (cmd, cmdtp->name, len) == 0) {
if (len == strlen (cmdtp->name))
return cmdtp; /* full match */

}

不懂的地方:

1、nand write 与 nand write.uboot 的不同。就是这个问题导致我想看u-boot源码的,之前用tftp下载uboot到内存,用nand write 命令然后烧写到nandflash里,启动不了,最后找到原因就是我应该用nand write.uboot来烧写uboot,nand write 与 nand write.uboot的区别

nand write.uboot 执行的是

if(!read && s != NULL && (!strcmp(s, ".uboot")) && nand->writesize == 8192)
            size=8192;
            ret=nand_write(nand, off, &size, (u_char *)addr);
            off+=8192;
            addr+=2048;
            ret=nand_write(nand, off, &size, (u_char *)addr);
            off+=8192;
            addr+=2048;
            ret=nand_write(nand, off, &size, (u_char *)addr);
            off+=8192;
            addr+=2048;
            ret=nand_write(nand, off, &size, (u_char *)addr);
            off+=8192;
            addr+=2048;
            size=1024*1024-4*8192;
            ret = nand_write(nand, off, &size, (u_char *)addr);

而nand write仅仅是

  nand_write(nand, off, &size, (u_char *)addr);

其中    nand为 struct mtd_info类型(MTD(memory technology device内存技术设备)) 由前面的  ret = nand_dump(nand, off);赋值。这里不太理解,nand write.uboot 写成好多行的原因我可以理解为uboot大小大过nand的一个页,但是这里偏移off加了8192 而addr 只加了2048 这是为什么呢?


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值