精华内容
参与话题
问答
  • 1.uboot启动内核的代码缩减如下:Uboot 1.16/lib_arm/board.c中start_armboot()函数调用/common/main.c中main_loop()函数,在main_loop()中有uboot启动内核的代码:s = getenv ("bootcmd");debug ("#...


    1.uboot启动内核的代码缩减如下:

    Uboot 1.16/lib_arm/board.cstart_armboot()函数调用/common/main.cmain_loop()函数,在main_loop()中有uboot启动内核的代码:

    s = getenv ("bootcmd");
    debug ("### main_loop: bootcmd=\"%s\"\n", s ? s :"<UNDEFINED>");
    if (bootdelay >= 0 && s && !abortboot (bootdelay))
    {
                    run_command(s, 0);
    }


    2.
    假设bootcmd = nandread.jffs2 0x30007FC0 kernel; bootm 0x30007FC0

    <1> nandread.jffs2 0x30007FC0 kernel

    nand读出内核:

    从哪里读?  kernel分区

    读到哪里去?0x30007FC0

     

    何为分区?

    简单的说就是将nand划分为几个区域,一般如下:

     

    bootloader->params->kernel->root

     

    这些分区划分在/include/configs/100ask24x0.h中写死的:

     

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



    进入uboot执行mtd ,可以查看已有分区:

    #       name                    大小              nand上的起始地址         

    0       bootloader        0x00040000               0x00000000

    1       params             0x00020000               0x00040000              

    2       kernel               0x00200000               0x00060000

    3       root                 0xfda00000               0x00260000

     

    上面的nand read.jffs2 0x30007FC0 kernel等价于:

    nand read.jffs20x30007FC0 0x00060000 0x00200000

    read.jffs2并不是指定特定的格式,仅表示不需要块/页对齐,所以kernel的分区大小可以随意定。

     

    <2> bootm0x30007FC0

    关键函数do_bootm()

     

    flash上存的内核:uImage

    uImage =头部+真正的内核

     

     

    头部的定义如下:


    typedef struct image_header {
            uint32_t       ih_magic;       

            uint32_t       ih_hcrc;        

            uint32_t       ih_time;       

            uint32_t       ih_size;       

            uint32_t       ih_load;      

            uint32_t       ih_ep;            

            uint32_t       ih_dcrc;      

            uint8_t               ih_os;         

            uint8_t               ih_arch;     

            uint8_t               ih_type;   

            uint8_t               ih_comp;   

            uint8_t              ih_name[IH_NMLEN];       

     } image_header_t;

     

    我们需要关心:

           uint32_t       ih_load;             

            uint32_t       ih_ep;              

    ih_load是加载地址,即内核运行是应该位于的地方   

    ih_ep是入口地址,即内核的入口地址

     

    这与uboot类似,uboot的加载地址是TEXT_BASE = 0x33F80000;入口地址是start.S中的_start

     

    nand读出来的内核可以放在ram中的任意地方,如0x310000000x32000000等等,只要它不破坏uboot所占用的内存空间就可以

     

    既然设定好了加载地址和入口地址,为什么内核还能随意放?

    因为uImage有一个头部!头部里有加载地址和入口地址,当我们用bootm xxx时,

    do_bootm先去读uImage的头部以获取该uImage的加载地址和入口地址,当发现该uImage目前所处的内存地址不等于它的加载地址时,会将uImage移动到它的加载地址上,代码中体现如下:

    uboot 1.16/common/cmd_bootm.c中的bootm_load_os()函数

    case IH_COMP_NONE:
    if (load != image_start)
    {
                    memmove_wd((void *)load, (void *)image_start, image_len, CHUNKSZ);
    }


    另外,当内核正好处于头部指定的加载地址,便不用ubootdo_bootm函数来帮我们搬运内核了,可以缩短启动时间。这就是为什么我们一般都下载uImage0x30007FC0的原因。

     

    内核加载地址是0x30008000,而头部的大小64个字节,将内核拷贝到0x30007FC0,加上头部的64个字节,内核正好位于0x30008000处。

     

    总结bootm做了什么:

    1.读取头部

    2.将内核移动到加载地址

    3.启动内核

     

    具体如何启动内核?

    使用/lib_arm/bootm.c定义的do_bootm_linux(),我们已经知道入口地址,只需跳到入口地址就可以启动linux内核了,在这之前需要做一件事———— uboot传递参数(启动参数)给内核。

    启动代码在do_bootm_linux()函数:

    void (*theKernel)(int zero, int arch,uint params);  //定义函数指针theKernel

           theKernel = (void (*)(int, int, uint))images->ep;    //先是将入口地址赋值给theKernel

           theKernel (0, bd->bi_arch_number, bd->bi_boot_params); //然后是调用thekernel,以0bd->bi_arch_numberbd->bi_boot_params为参数

     

    下面分析这三个参数:

    1.  0—相当于mov,ro #0

    2.bd->bi_arch_number:uboot机器码,这个在/board/100ask24x0.c设置:gd->bd->bi_arch_number = MACH_TYPE_S3C2440,MACH_TYPE_S3C2440在/arch/arm/asm/mach-types.h定义362,内核机器码和uboot机器码必须一致才能启动内核

     

    2. bd->bi_boot_parmas--- 启动参数地址

    也是在在/board/100ask24x0.c设置:gd->bd->bi_boot_params = 0x30000100;

     

    启动参数(tag)在哪里设置?

    在lib_arm/armlinux.c设置:

    setup_start_tag (bd);
    setup_revision_tag (parmas);
    setup_memory_tags (bd);
    setup_commandline_tag (bd, commandline);
    setup_initrd_tag (bd, images->rd_start, images->rd_end);
    setup_videolfb_tag ((gd_t *) gd);
    setup_end_tag (bd);


    每一个启动参数对应一个tag结构体,所谓的设置传递参数其实就是初始化这些tag的值,想了解这个结构体以及这些tag的值是如何设置的请看嵌入式Linux应用开发完全手册uboot章节

     

    我们来看setup_start_tag(bd)函数:

    static void setup_start_tag (bd_t *bd)
    {
            params = (struct tag *) bd->bi_boot_params;  
            params->hdr.tag = ATAG_CORE;
            params->hdr.size = tag_size(tag_core);
            params->u.core.flags = 0;
            params->u.core.pagesize = 0;
            params->u.core.rootdev = 0;
            params = tag_next (params);
    }


    再看setup_commandline_tag (bd , commandline)

    static void setup_commandline_tag (bd_t *bd, char*commandline)
    {
     // commandline
    就是我们的bootargs
            char *p;
            if (!commandline)
                    return;
            for (p = commandline; *p == ' '; p++);
            if (*p == '\0')
                    return;
            params->hdr.tag = ATAG_CMDLINE;
            params->hdr.size =
                    (sizeof(struct tag_header) + strlen (p) + 1 + 4) >> 2;
            strcpy (params->u.cmdline.cmdline, p);
            params = tag_next (params);
    }

     

    内核启动时会读取这些tag(参数)并跳转启动。

     

    更多uboot启动内核的细节观看毕业班视频自己写uboot。

    展开全文
  • :...1.uboot启动内核的代码缩减如下: s = getenv ("bootcmd"); debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : ""); if (bootdelay >= 0 && s && !abortboot (bootdel

    转自:http://www.arm9home.net/read.php?tid-14917.html

    1.uboot启动内核的代码缩减如下:
    s = getenv ("bootcmd");
    debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
    if (bootdelay >= 0 && s && !abortboot (bootdelay))
    {
    run_command (s, 0);
    }

    2.假设bootcmd = nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0
    <1> nand read.jffs2 0x30007FC0 kernel
    nand read.jffs2 0x30007FC0 kernel;
    从nand读出内核:从哪里读? 从kernel分区
    放到哪里去?-0x30007FC0

    下面讲解什么是分区:
    就是将nand划分为几个区域,一般如下:
    bootloader-》params-》kernel-》root

    这些分区的划分是在/include/configs/mini2440.h中写死的:
    #define MTDPARTS_DEFAULT "mtdparts=nandflash0:250k@0(bootloader)," \
    "128k(params)," \
    "5m(kernel)," \
    "-(root)"

    注:@0表示从0地址开始,250k的bootloader分区可能对某些uboot不够用,这里只是举例而已。
    将上面的信息换算成十六进制:
    # name 大小 在nand上的起始地址
    0 bootloader 0x00040000 0x00000000
    1 params 0x00020000 0x00040000
    2 kernel 0x00200000 0x00060000
    3 root 0xfda00000 0x00260000

    那么上面的nand read.jffs2 0x30007FC0 kernel就等价于:
    nand read.jffs2 0x30007FC0 0x00060000 0x00200000
    注:这里的read.jffs2并不是指定要什么特定的格式,而是用read.jffs2不需要块/页对齐,所以这个kernel的分区大小可以
    随意定。

    <2> bootm 0x30007FC0
    关键函数do_bootm()

    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;
    我们需要关心的是:
    uint32_t ih_load; /* Data Load Address */
    uint32_t ih_ep; /* Entry Point Address */
    ih_load是加载地址,即内核运行是应该位于的地方
    ih_ep是入口地址,即内核的入口地址


    这与uboot是类似的,uboot的加载地址是TEXT_BASE = 0x33F80000;入口地址是start.S中的_start。

    其实我们把内核中nand读出来的时候是可以放在内核的任何地方的,如0x31000000,0x32000000等等,只要它不破坏uboot所占用的内存空间就可以了,如下图:
    从0x33F4DF74-0x30000000都是可以用的。

    那么为什么既然设定好了加载地址和入口地址内核还能随意放呢?
    那是因为uImage有一个头部!头部里有加载地址和入口地址,当我们用bootm xxx的时候,
    do_bootm这个函数会先去读uImage的头部以获取该uImage的加载地址和入口地址,当发现该uImage目前所处的内存地址不等于它的加载地址时,该函数会将该uImage移动到它的加载地址上,在代码中体现如下:
    case IH_COMP_NONE::
    if (load != image_start)
    {
    memmove_wd ((void *)load, (void *)image_start, image_len, CHUNKSZ);
    }
    另外,当我们的内核正好处于头部指定的加载地址的话,那么就不用uboot的do_bootm函数来帮我们搬运内核了,这样可以节省启动时间。这就是为什么我们一般都下载uImage到
    0x30007FC0的原因了!

    我们所用的内核加载地址是0x30008000,而头部的大小为64个字节,所以将内核拷贝到0x30007FC0时,再加载头部的64个字节,内核正好位于0x30008000处!


    现在总结bootm做了什么:
    1. 读取头部
    2. 将内核移动到加载地址
    3. 启动内核

    具体如何启动内核?
    使用do_bootm_linux(),在/lib_arm/bootm.c定义,因为我们已经知道入口地址了,所以只需跳到入口地址就可以启动linux内核了,但是在这之前需要做一件事————uboot传递参数给内核!!
    现在来分析do_bootm_linux()这个函数:
    theKernel = (void (*)(int, int, uint))images->ep;//先是将入口地址赋值给theKernel
    theKernel (0, machid, bd->bi_boot_params);//然后是调用thekernel
    函数,以0,machid,bd->bi_boot_params作为参数
    下面分析这三个参数:
    1.machid就是uboot里设置好的板子的机器码,mini2440的是MACH_TYPE_MINI2440 (1999),内核所设置的机器码和uboot所设置的机器码必须一致才能启动内核
    2.bd->bi_boot_parmas就是uboot需传递给内核的启动参数所位于的地址
    3.0暂时还不知道什么作用/**********************************************/

    那么uboot传给内核的启动参数是在哪里设置的呢?
    其实就是在调用 theKernel (0, machid, bd->bi_boot_params);前面的一小段代码里设置的,下面我截取了部分片段:
    setup_start_tag (bd);
    setup_revision_tag (&params);
    setup_memory_tags (bd);
    setup_commandline_tag (bd, commandline);
    setup_initrd_tag (bd, images->rd_start, images->rd_end);
    setup_videolfb_tag ((gd_t *) gd);
    setup_end_tag (bd);
    每一个启动参数对应一个tag结构体,所谓的设置传递参数其实就是初始化这些tag的值,想了解这个结构体以及这些tag的值是如何设置的请看韦东山的书关于uboot移植章节!
    下面我们看一下setup_start_tag(bd)这个函数先:
    static void setup_start_tag (bd_t *bd)
    {
    params = (struct tag *) bd->bi_boot_params;
    //在board.c中有一句gd->bd->bi_boot_params = 0x30000100,这里设置了参数存放的位置

    params->hdr.tag = ATAG_CORE;
    params->hdr.size = tag_size (tag_core);

    params->u.core.flags = 0;
    params->u.core.pagesize = 0;
    params->u.core.rootdev = 0;

    params = tag_next (params);
    }
    我们再来看下setup_commandline_tag (bd, commandline);这个函数:
    static void setup_commandline_tag (bd_t *bd, char *commandline)
    {
    // commandline就是我们的bootargs
    char *p;
    if (!commandline)
    return;
    for (p = commandline; *p == ' '; p++);
    if (*p == '\0')
    return;
    params->hdr.tag = ATAG_CMDLINE;
    params->hdr.size =
    (sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2;
    strcpy (params->u.cmdline.cmdline, p);
    params = tag_next (params);
    }
    Linux内核启动时就会去读取这些tag参数

    展开全文
  • 这个芯片是NXP4330,目前是高通的一个芯片,基于ARM-contexA9架构,那么就跟4412是一样的架构了,今天将uboot加载流程基本上算是搞明白了,也明白了uboot最后是通过一些手段,最终能够去加载kernel.img,最终启动内核...

          今天在工作上搞了一天恩智浦的芯片uboot程序,目的是希望将一个裸板的程序移植到uboot中,并且开机让它运行。这个芯片是NXP4330,目前是恩智浦的一个芯片,基于ARM-contexA9架构,那么就跟4412是一样的架构了,今天将uboot加载流程基本上算是搞明白了,也明白了uboot最后是通过一些手段,最终能够去加载kernel.img,最终启动内核,后面就是加载文件系统了。

         心血来潮,所以,今天借这个机会在说明一下4412的uboot最后是怎么去获取kernel.img进而启动,其实都大同小异,搞明白一个其它的就类似的了。

         以前有写过一篇文章,关于4412的uboot启动流程的汇编分析:http://blog.csdn.net/morixinguan

         在这篇文章中,我们分析了4412 uboot的核心,最后通过以下代码调用了C中的函数:

         这就算是完成了uboot启动的第一个阶段:

         start_code -> cpu_init_crit -> call_board_init_f -> board_init_f 

    call_board_init_f:
    	ldr	sp, =(CONFIG_SYS_INIT_SP_ADDR)
    	bic	sp, sp, #7 /* 8-byte alignment for ABI compliance */
    	ldr	r0,=0x00000000
    	bl	board_init_f
         从代码中我们可以看到,程序最终是bl board_init_f,跳转到这个函数去执行,我们跟到这个函数如下:

         它的位置位于arch/arm/lib/board.c

    void board_init_f(ulong bootflag)
    {
    	bd_t *bd;
    	init_fnc_t **init_fnc_ptr;
    	gd_t *id;
    	ulong addr, addr_sp;
    
    	/* Pointer is writable since we allocated a register for it */
    	gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07);
    
    	/* compiler optimization barrier needed for GCC >= 3.4 */
    	__asm__ __volatile__("": : :"memory");
    
    	memset((void*)gd, 0, sizeof (gd_t));
    
    	gd->mon_len = _bss_end_ofs;
    
    	for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
    		if ((*init_fnc_ptr)() != 0) {
    			hang();
    		}
    	}
    
    	debug ("monitor len: %08lX\n", gd->mon_len);
    	/*
    	 * Ram is setup, size stored in gd !!
    	 */
    	debug ("ramsize: %08lX\n", gd->ram_size);
    #if defined(CONFIG_SYS_MEM_TOP_HIDE)
    	/*
    	 * Subtract specified amount of memory to hide so that it won't
    	 * get "touched" at all by U-Boot. By fixing up gd->ram_size
    	 * the Linux kernel should now get passed the now "corrected"
    	 * memory size and won't touch it either. This should work
    	 * for arch/ppc and arch/powerpc. Only Linux board ports in
    	 * arch/powerpc with bootwrapper support, that recalculate the
    	 * memory size from the SDRAM controller setup will have to
    	 * get fixed.
    	 */
    	gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE;
    #endif
    
    	addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size;
    
    #ifdef CONFIG_LOGBUFFER
    #ifndef CONFIG_ALT_LB_ADDR
    	/* reserve kernel log buffer */
    	addr -= (LOGBUFF_RESERVE);
    	debug ("Reserving %dk for kernel logbuffer at %08lx\n", LOGBUFF_LEN, addr);
    #endif
    #endif
    
    #ifdef CONFIG_PRAM
    	/*
    	 * reserve protected RAM
    	 */
    	i = getenv_r("pram", (char *)tmp, sizeof (tmp));
    	reg = (i > 0) ? simple_strtoul((const char *)tmp, NULL, 10) : CONFIG_PRAM;
    	addr -= (reg << 10);		/* size is in kB */
    	debug ("Reserving %ldk for protected RAM at %08lx\n", reg, addr);
    #endif /* CONFIG_PRAM */
    
    #if !(defined(CONFIG_SYS_NO_ICACHE) && defined(CONFIG_SYS_NO_DCACHE))
    	/* reserve TLB table */
    	addr -= (4096 * 4);
    
    	/* round down to next 64 kB limit */
    	addr &= ~(0x10000 - 1);
    
    	gd->tlb_addr = addr;
    	debug ("TLB table at: %08lx\n", addr);
    #endif
    
    	/* round down to next 4 kB limit */
    	addr &= ~(4096 - 1);
    	debug ("Top of RAM usable for U-Boot at: %08lx\n", addr);
    
    #ifdef CONFIG_VFD
    #	ifndef PAGE_SIZE
    #	  define PAGE_SIZE 4096
    #	endif
    	/*
    	 * reserve memory for VFD display (always full pages)
    	 */
    	addr -= vfd_setmem(addr);
    	gd->fb_base = addr;
    #endif /* CONFIG_VFD */
    
    #ifdef CONFIG_LCD
    	/* reserve memory for LCD display (always full pages) */
    	addr = lcd_setmem(addr);
    	gd->fb_base = addr;
    #endif /* CONFIG_LCD */
    
    	/*
    	 * reserve memory for U-Boot code, data & bss
    	 * round down to next 4 kB limit
    	 */
    	addr -= gd->mon_len;
    	addr &= ~(4096 - 1);
    
    #if defined(CONFIG_S5P) || defined(CONFIG_S5P6450)
    	addr = CONFIG_SYS_LOAD_ADDR;
    #endif
    
    	debug ("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10, addr);
    
    #ifndef CONFIG_PRELOADER
    	/*
    	 * reserve memory for malloc() arena
    	 */
    	addr_sp = addr - TOTAL_MALLOC_LEN;
    	debug ("Reserving %dk for malloc() at: %08lx\n",
    			TOTAL_MALLOC_LEN >> 10, addr_sp);
    	/*
    	 * (permanently) allocate a Board Info struct
    	 * and a permanent copy of the "global" data
    	 */
    	addr_sp -= sizeof (bd_t);
    	bd = (bd_t *) addr_sp;
    	gd->bd = bd;
    	debug ("Reserving %zu Bytes for Board Info at: %08lx\n",
    			sizeof (bd_t), addr_sp);
    	addr_sp -= sizeof (gd_t);
    	id = (gd_t *) addr_sp;
    	debug ("Reserving %zu Bytes for Global Data at: %08lx\n",
    			sizeof (gd_t), addr_sp);
    
    	/* setup stackpointer for exeptions */
    	gd->irq_sp = addr_sp;
    #ifdef CONFIG_USE_IRQ
    	addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);
    	debug ("Reserving %zu Bytes for IRQ stack at: %08lx\n",
    		CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ, addr_sp);
    #endif
    
    	/* leave 3 words for abort-stack    */
    	addr_sp -= 3;
    
    	/* 8-byte alignment for ABI compliance */
    	addr_sp &= ~0x07;
    #else
    	addr_sp += 128;	/* leave 32 words for abort-stack   */
    	gd->irq_sp = addr_sp;
    #endif
    
    	debug ("New Stack Pointer is: %08lx\n", addr_sp);
    
    #ifdef CONFIG_POST
    	post_bootmode_init();
    	post_run(NULL, POST_ROM | post_bootmode_get(0));
    #endif
    
    	gd->bd->bi_baudrate = gd->baudrate;
    	/* Ram ist board specific, so move it to board code ... */
    	dram_init_banksize();
    	display_dram_config();	/* and display it */
    
    	gd->relocaddr = addr;
    	gd->start_addr_sp = addr_sp;
    	gd->reloc_off = addr - _TEXT_BASE;
    	debug ("relocation Offset is: %08lx\n", gd->reloc_off);
    	memcpy(id, (void *)gd, sizeof (gd_t));
    
    	relocate_code(addr_sp, id, addr);
    	/* NOTREACHED - relocate_code() does not return */
    }
    第二阶段的起始代码 arch/arm/lib/board.c 里的 void board_init_r(gd_t *id, ulong dest_addr) 函数

    其中, id 是从 board_init_f 里面获取,dest_addr也就是链接u-boot的地址

    void board_init_r(gd_t *id, ulong dest_addr)
    {
    	char *s;
    	bd_t *bd;
    	ulong malloc_start;
    #if !defined(CONFIG_SYS_NO_FLASH)
    	ulong flash_size;
    #endif
    
    	gd = id;
    	bd = gd->bd;
    
    	gd->flags |= GD_FLG_RELOC;	/* tell others: relocation done */
    
    	monitor_flash_len = _bss_start_ofs;
    	debug ("monitor flash len: %08lX\n", monitor_flash_len);
    	board_init();	/* Setup chipselects */
    
    #ifdef CONFIG_SERIAL_MULTI
    	//serial_initialize();
    #endif
    
    	debug ("Now running in RAM - U-Boot at: %08lx\n", dest_addr);
    
    #ifdef CONFIG_LOGBUFFER
    	logbuff_init_ptrs();
    #endif
    #ifdef CONFIG_POST
    	post_output_backlog();
    #endif
            //这里对内存进行了分配
    	/* The Malloc area is immediately below the monitor copy in DRAM */
    	malloc_start = dest_addr - TOTAL_MALLOC_LEN;
    	mem_malloc_init(malloc_start, TOTAL_MALLOC_LEN);
    
    #if !defined(CONFIG_SYS_NO_FLASH)
    	puts("FLASH:\t");
    
    	if ((flash_size = flash_init()) > 0) {
    # ifdef CONFIG_SYS_FLASH_CHECKSUM
    		print_size(flash_size, "");
    		/*
    		 * Compute and print flash CRC if flashchecksum is set to 'y'
    		 *
    		 * NOTE: Maybe we should add some WATCHDOG_RESET()? XXX
    		 */
    		s = getenv("flashchecksum");
    		if (s && (*s == 'y')) {
    			printf("  CRC: %08X",
    				crc32 (0, (const unsigned char *) CONFIG_SYS_FLASH_BASE, flash_size)
    			);
    		}
    		putc('\n');
    # else	/* !CONFIG_SYS_FLASH_CHECKSUM */
    		print_size(flash_size, "\n");
    # endif /* CONFIG_SYS_FLASH_CHECKSUM */
    	} else {
    		puts(failed);
    		hang();
    	}
    #endif
    
    #if defined(CONFIG_CMD_NAND)
    	puts("NAND:\t");
    	nand_init();		/* go init the NAND */
    #endif
    
    #if defined(CONFIG_CMD_ONENAND)
    	onenand_init();
    #endif
    
    #ifdef CONFIG_GENERIC_MMC
    	mmc_initialize(bd);
    #endif
    
    #ifdef CONFIG_HAS_DATAFLASH
    	AT91F_DataflashInit();
    	dataflash_print_info();
    #endif
    
    	/* initialize environment */
    	env_relocate();
    
    #ifdef CONFIG_VFD
    	/* must do this after the framebuffer is allocated */
    	drv_vfd_init();
    #endif /* CONFIG_VFD */
    
    	/* IP Address */
    	gd->bd->bi_ip_addr = getenv_IPaddr("ipaddr");
    
    	stdio_init();	/* get the devices list going. */
    
    	jumptable_init();
    
    #if defined(CONFIG_API)
    	/* Initialize API */
    	api_init();
    #endif
    
    	//console_init_r();	/* fully init console as a device */
    
    #if defined(CONFIG_ARCH_MISC_INIT)
    	/* miscellaneous arch dependent initialisations */
    	arch_misc_init();
    #endif
    #if defined(CONFIG_MISC_INIT_R)
    	/* miscellaneous platform dependent initialisations */
    	misc_init_r();
    #endif
    
    	 /* set up exceptions */
    	interrupt_init();
    	/* enable exceptions */
    	enable_interrupts();
    
    	/* Perform network card initialisation if necessary */
    #if defined(CONFIG_DRIVER_SMC91111) || defined(CONFIG_DRIVER_LAN91C96)
    	/* XXX: this needs to be moved to board init */
    	if (getenv("ethaddr")) {
    		uchar enetaddr[6];
    		eth_getenv_enetaddr("ethaddr", enetaddr);
    		smc_set_mac_addr(enetaddr);
    	}
    #endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */
    
    #if defined(CONFIG_DRIVER_DM9000)
    	/* XXX: this needs to be moved to board init */
    	if (getenv("ethaddr")) {
    		uchar enetaddr[6];
    		eth_getenv_enetaddr("ethaddr", enetaddr);
    		dm9000_set_mac_addr(enetaddr);
    	}
    #endif
    
    	/* Initialize from environment */
    	if ((s = getenv("loadaddr")) != NULL) {
    		load_addr = simple_strtoul(s, NULL, 16);
    	}
    #if defined(CONFIG_CMD_NET)
    	if ((s = getenv("bootfile")) != NULL) {
    		copy_filename(BootFile, s, sizeof (BootFile));
    	}
    #endif
    
    #ifdef BOARD_LATE_INIT
    	board_late_init();
    #endif
    
    #ifdef CONFIG_BITBANGMII
    	bb_miiphy_init();
    #endif
    #if defined(CONFIG_CMD_NET)
    #if defined(CONFIG_NET_MULTI)
    	puts("Net:\t");
    #endif
    	eth_initialize(gd->bd);
    #if defined(CONFIG_RESET_PHY_R)
    	debug ("Reset Ethernet PHY\n");
    	reset_phy();
    #endif
    #endif
    
    #ifdef CONFIG_POST
    	post_run(NULL, POST_RAM | post_bootmode_get(0));
    #endif
    
    #if defined(CONFIG_PRAM) || defined(CONFIG_LOGBUFFER)
    	/*
    	 * Export available size of memory for Linux,
    	 * taking into account the protected RAM at top of memory
    	 */
    	{
    		ulong pram;
    		uchar memsz[32];
    #ifdef CONFIG_PRAM
    		char *s;
    
    		if ((s = getenv("pram")) != NULL) {
    			pram = simple_strtoul(s, NULL, 10);
    		} else {
    			pram = CONFIG_PRAM;
    		}
    #else
    		pram=0;
    #endif
    #ifdef CONFIG_LOGBUFFER
    #ifndef CONFIG_ALT_LB_ADDR
    		/* Also take the logbuffer into account (pram is in kB) */
    		pram += (LOGBUFF_LEN+LOGBUFF_OVERHEAD)/1024;
    #endif
    #endif
    		sprintf((char *)memsz, "%ldk", (bd->bi_memsize / 1024) - pram);
    		setenv("mem", (char *)memsz);
    	}
    #endif
    	//从这里开始就进入了uboot的死循环,这里就是所谓的uboot main函数了
    	/* 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 */
    }
    
          由于上面代码过多,我们从代码的注释大致就可以看出,上面是对串口,内存,以及平台相关的东西进行了设置和初始化,到代码的最后我们看到了以下死循环:

    for (;;) {
    		main_loop();
    	}
         我们接着跟到main_loop();这个函数:

         这个函数的位置位于:common/main.c

         main_loop()函数做的都是与具体平台无关的工作,主要包括初始化启动次数限制机制、设置软件版本号、打印启动信息、解析命令等。

      (1)设置启动次数有关参数。在进入main_loop()函数后,首先是根据配置加载已经保留的启动次数,并且根据配置判断是否超过启动次数。代码如下:

    void main_loop (void)
    {
    #ifndef CONFIG_SYS_HUSH_PARSER
    	static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, };
    	int len;
    	int rc = 1;
    	int flag;
    #endif
    
    #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
    	char *s;
    	int bootdelay;
    #endif
    #ifdef CONFIG_PREBOOT
    	char *p;
    #endif
    #ifdef CONFIG_BOOTCOUNT_LIMIT
    	unsigned long bootcount = 0;
    	unsigned long bootlimit = 0;
    	char *bcs;
    	char bcs_set[16];
    #endif /* CONFIG_BOOTCOUNT_LIMIT */
    
    #if defined(CONFIG_VFD) && defined(VFD_TEST_LOGO)
    	ulong bmp = 0;		/* default bitmap */
    	extern int trab_vfd (ulong bitmap);
    
    #ifdef CONFIG_MODEM_SUPPORT
    	if (do_mdm_init)
    		bmp = 1;	/* alternate bitmap */
    #endif
    	trab_vfd (bmp);
    #endif	/* CONFIG_VFD && VFD_TEST_LOGO */
    
    #ifdef CONFIG_BOOTCOUNT_LIMIT
    	bootcount = bootcount_load();  // 加载保存的启动次数
    	bootcount++;		// 启动次数加1        
    	bootcount_store (bootcount);  // 更新启动次数 
    	sprintf (bcs_set, "%lu", bootcount);// 打印启动次数  
    	setenv ("bootcount", bcs_set); 
    	bcs = getenv ("bootlimit"); //获取环境变量,用于引导
    	bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0;
    #endif /* CONFIG_BOOTCOUNT_LIMIT */
    
    #ifdef CONFIG_MODEM_SUPPORT
    	debug ("DEBUG: main_loop:   do_mdm_init=%d\n", do_mdm_init);
    	if (do_mdm_init) {
    		char *str = strdup(getenv("mdm_cmd"));// 获取Modem参数 
    		setenv ("preboot", str);  /* set or delete definition */ //设置环境变量
    		if (str != NULL)
    			free (str);
    		mdm_init(); /* wait for modem connection */
    	}
    #endif  /* CONFIG_MODEM_SUPPORT */
    //接下来设置U-Boot的版本号,初始化命令自动完成功能等。代码如下:
    #ifdef CONFIG_VERSION_VARIABLE   
    	{
    		extern char version_string[];
    
    		setenv ("ver", version_string);  /* set version variable */ // 设置版本号
    	}
    #endif /* CONFIG_VERSION_VARIABLE */
    
    #ifdef CONFIG_SYS_HUSH_PARSER
    	u_boot_hush_start ();//初始化hash功能
    
    #endif
    
    #if defined(CONFIG_HUSH_INIT_VAR)
    	hush_init_var (); 
    #endif
    
    #ifdef CONFIG_AUTO_COMPLETE
    	install_auto_complete(); // 初始化命令自动完成功能
    #endif
    
    #ifdef CONFIG_PREBOOT
    	if ((p = getenv ("preboot")) != NULL) {
    # ifdef CONFIG_AUTOBOOT_KEYED
    		int prev = disable_ctrlc(1);	/* disable Control C checking */// 关闭Crtl+C组合键
    # endif
    
    # ifndef CONFIG_SYS_HUSH_PARSER
    		run_command (p, 0);// 运行Boot参数  
    # else
    		parse_string_outer(p, FLAG_PARSE_SEMICOLON |
    				    FLAG_EXIT_FROM_LOOP);
    # endif
    
    # ifdef CONFIG_AUTOBOOT_KEYED
    		disable_ctrlc(prev);	/* restore Control C checking */ // 恢复Ctrl+C组合键
    # endif
    	}
    #endif /* CONFIG_PREBOOT */
    
    #if defined(CONFIG_UPDATE_TFTP)
    	update_tftp ();
    #endif /* CONFIG_UPDATE_TFTP */
    
    #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
    	s = getenv ("bootdelay");
    	bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
    
    	debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);
    
    # ifdef CONFIG_BOOT_RETRY_TIME
    	init_cmd_timeout ();
    # endif	/* CONFIG_BOOT_RETRY_TIME */
    
    #ifdef CONFIG_POST
    	if (gd->flags & GD_FLG_POSTFAIL) {
    		s = getenv("failbootcmd");
    	}
    	else
    #endif /* CONFIG_POST */
    #ifdef CONFIG_BOOTCOUNT_LIMIT
    	if (bootlimit && (bootcount > bootlimit)) { // 检查是否超出启动次数限制
    		printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",
    		        (unsigned)bootlimit);
    		s = getenv ("altbootcmd");
    	}
    	else
    #endif /* CONFIG_BOOTCOUNT_LIMIT */
    		s = getenv ("bootcmd");
    
    	debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
    
    	if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
    # ifdef CONFIG_AUTOBOOT_KEYED
    		int prev = disable_ctrlc(1);	/* disable Control C checking */
    # endif
    
    # ifndef CONFIG_SYS_HUSH_PARSER
    		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
    	}
    
    # ifdef CONFIG_MENUKEY
    	if (menukey == CONFIG_MENUKEY) { // 检查是否支持菜单键  
    	    s = getenv("menucmd");
    	    if (s) {
    # ifndef CONFIG_SYS_HUSH_PARSER
    		run_command (s, 0);
    # else
    		parse_string_outer(s, FLAG_PARSE_SEMICOLON |
    				    FLAG_EXIT_FROM_LOOP);
    # endif
    	    }
    	}
    #endif /* CONFIG_MENUKEY */
    #endif /* CONFIG_BOOTDELAY */
    
    	/*
    	 * Main Loop for Monitor Command Processing
    	 */
    #ifdef CONFIG_SYS_HUSH_PARSER
    	parse_file_outer();
    	/* This point is never reached */
    	for (;;);
    #else
    	for (;;) {
    #ifdef CONFIG_BOOT_RETRY_TIME
    		if (rc >= 0) {
    			/* Saw enough of a valid command to
    			 * restart the timeout.
    			 */
    			reset_cmd_timeout();
    		}
    #endif
    		len = readline (CONFIG_SYS_PROMPT);// 读取命令  
    
    		flag = 0;	/* assume no special flags for now */
    		if (len > 0)
    			strcpy (lastcommand, console_buffer);
    		else if (len == 0)
    			flag |= CMD_FLAG_REPEAT;
    #ifdef CONFIG_BOOT_RETRY_TIME
    		else if (len == -2) {
    			/* -2 means timed out, retry autoboot
    			 */
    			puts ("\nTimed out waiting for command\n");
    # ifdef CONFIG_RESET_TO_RETRY
    			/* Reinit board to run initialization code again */
    			do_reset (NULL, 0, 0, NULL);
    # else
    			return;		/* retry autoboot */
    # endif
    		}
    #endif
    
    		if (len == -1)
    			puts ("<INTERRUPT>\n");
    		else
    			rc = run_command (lastcommand, flag);// 运行命令  
    
    		if (rc <= 0) {
    			/* invalid command or not repeatable, forget it */
    			lastcommand[0] = 0;
    		}
    	}
    #endif /*CONFIG_SYS_HUSH_PARSER*/
    }
        至此,uboot的加载流程大致就清楚了,如果我的硬件裸板测试程序要移植到uboot里面,然后将uboot重新启动,直接到裸板的测试程序,我们就可以利用上面这个机制,可以将所有的main_loop();函数的代码都去除,然后将硬件测试程序放在main_loop中,因为它是一个死循环,因此会周而复始的进行。




    展开全文
  • 关于Uboot自启动的问题方案解决

    千次阅读 2017-01-12 15:29:29
    本文主要针对于在Uboot如何设置自启动的问题 一:Uboot原先的环境变量贴出: => print bootcmd=mmc dev2;mmc dev 2 ;if mmc rescan; then if run loadbootscript; then runbootscript; else if run loadimage...

    本文主要针对于在Uboot下如何设置自启动的问题

    一:Uboot原先的环境变量贴出:

    => print

    bootcmd=mmc dev2;mmc dev 2 ;if mmc rescan; then if run loadbootscript; then runbootscript; else if run loadimage; then run mmcboot; else runnetboot; fi; fi; else run netboot;fi

    二:需要自启动的代码:

    setenv fdt_file zImage-imx6sx-sdb-m4.dtb

    fatload mmc 2:1 0x80000000 XCU.bin

    dcache flush

    cp.b 0x80000000 0x7F8000 0x8000

    dcache flush

    setenv mmcargs "setenv bootargs uart_from_oscconsole=${console},${baudrate} root=${mmcroot}"

    bootaux 0x7F8000

    三:uboot环境变量设置

    => setenv fdt_filezImage-imx6sx-sdb-m4.dtb

    =>setenv mmcargs "setenvbootargs uart_from_osc console=${console},${baudrate}root=${mmcroot}"


    =>setenv bootrtos "fatloadmmc 2:1 0x80000000 XCU.bin;dcache flush;cp.b 0x80000000 0x7F80000x8000;dcache flush;bootaux 0x7F8000"

    => setenv bootcmd "mmcdev 2;mmc dev 2 ;run bootrtos ; if mmc rescan; then if runloadbootscript; then run bootscript; else if run loadimage; thenrun mmcboot; else run netboot; fi; fi; else run netboot;fi"


    => saveenv

    三:重新进入Uboot

    => print

    bootcmd=mmc dev 2;mmc dev 2 ;run bootrtos ;if mmc rescan; thenif run loadbootscript; then run bootscript; else if run loadimage;then run mmcboot; else run netboot; fi; fi; else run netboot;fi

    bootrtos = fatloadmmc 2:1 0x80000000 XCU.bin;dcache flush;cp.b 0x80000000 0x7F80000x8000;dcache flush;bootaux 0x7F8000

    四:重启(不进入uboot环境)

    RTOS端的信息打印出来看是否启动


    解释:由于未连接CAN数据发送设备,所以其Send_len0,但是说明其已经启动

    关于Bootcmd的相关信息可以从百度获取,在这主要说明其开机后默认是执行Bootcmd命令的。

    思路是:将所有的命令保存到一个环境变量中(这里是bootrtos),然后通过run命令执行这个环境变量(runbootrtos)看是否能够执行,若能执行说明OK,若不能命令出错;然后将run命令集合也添加到bootcmd中,注意这里的ifelse语句。开机重启OK

    展开全文
  • UBOOT如何从norflash和nandflash启动

    千次阅读 2017-01-11 10:36:57
    :http://www.aiuxian.com/article/p-2796357.html   电子产品如果没有了电,就跟废品没什么区别,是电赋予了他们生命,然而程序则是他们的灵魂。 小时候一直很好奇,一个个死板的电子产品为什么一上...
  • 在做uboot从nandflash启动时,需要检测硬件启动方式,启动方式是由硬件OM管脚决定的,软件无法检测硬件电平状态,但可以根据CPU的启动特性来检测。 通过深入研究2440的启动方式,总结了几点: 如果将S3C...
  • Uboot如何引导系统

    千次阅读 2015-06-09 11:11:38
    启动到U-Boot的时候,会看到 Hit any key to stop autoboot:0 复制代码 按下任意键则放弃了自动引导,从而进入了U-Boot的命令行。 在这里你可以手动进行接下来系统的引导。但是我不会。 虽然不会,
  • Uboot 启动logo

    2011-08-22 15:46:00
    如何更改Uboot启动logo:1、进入tools目录,在该目录下你会看到一个叫logos的目录,里面存的是logo图标,可以自己添加logo图标到该目录,注意需256色的。 2、打开tools目录下的Makefile文件,在第44行至48行间,有...
  • 分析uboot启动过程

    2017-12-26 22:41:57
    相比于linux操作系统,uboot本身不大,能够自启动,作为嵌入式设备的引导启动,是个好的选择。此外,它具有源码开放、支持多种嵌入式操作系统、丰富的设备驱动源码等特点。 作用: 1)、为系统启动之前初始化硬件设备...
  • 这篇文章主要描述如何把2020.10版本的uboot在tiny4412核心板(1412版)上启动起来,而不关注一些细节问题。 一、添加交叉编译器信息到Makefile 打开uboot源代码根目录下的Makefile,在开头添加如下: ARCH = arm ...
  • 前两天611的Nandboot搞定了,但Nandboot有时会boot不起来,用ICE查看esram,发现前8K有错。  为了使以后再Nandboot出现问题的时候,...以后可以将这种方式做成命令,故而学习了一下如何uboot中添加自己的命令。
  • uboot env ethaddr 是如何生成的? 思考 默认环境变量中并不会指定 ethaddr , 然而板子起来后会自己生成ethaddr 变量,并且各个板子的ethaddr 是唯一的。并且即使清除env分区,重新启动后ethaddr 也不会变。是如何...
  • 只是找到了比较一点点的线索, 如下: ...1. Uboot:从flash启动set bootcmd nand read 0x30000000 0x60000 0x200000\;bootm 0x30000000set bootargs noinitrd root=/dev/mtdblock3 init=/linuxrc consol...
  • 1.uboot中的环境变量 bootdelay:执行自动启动的等候秒数 baudrate:串口控制台的波特率 netmask: 以太网接口的掩码 ethaddr: 以太网卡的网卡物理地址 bootfile: 缺省的下载文件 bootargs:传递给内核的启动参数 ...
  • 前面已经将网卡配置好,也已经可以通过tftp下载程序到nand flash,也可以通过nfs挂载根文件系统,可以说基本的开发环境已经搭建好。本节介绍如何从sd卡启动自己已有源码的uboot
  • 平台:STi7162 uboot版本:uboot 1.3.1(st已做过修改)   st平台的内存首地址是0x80000000,此地址不是固定的,不同类型的芯片是不同的,比如samsung的arm920t是0x...工作在启动加载模式时,uboot会自动执行boot
  • 前面讲了Uboot启动流程和如何修改调试串口,相信大家对Uboot已经有了初步的了解,今天来进行更深一点的分析。上篇文章 OK6410开发板Uboot学习总结----(二)修改调试打印串口 遗留一个问题:烧写文件时Uboot还是使用...
  • uboot环境变量

    2013-06-27 16:25:15
    编好了u-boot,但是如何来使用确不是那么简单的,u-boot的环境变量经常设置错误而导致内核启动失败。 u-boot的环境变量是使用u-boot的关键,它可以由你自己定义的,但是其中有一些也是大家经常使用,约定俗成的,有...
  • 前章我们已经可以制作出用来启动的SD卡了,并将自己编译的UBOOT烧录到了SD卡中这份UBOOT代码中的内存配置肯定和手头的开发板不一致,这里我们将搞清楚如何修改内存大小,并说清楚内存大小到底是如何计算的一、关于...

空空如也

1 2 3 4
收藏数 78
精华内容 31
关键字:

uboot如何自启动