• uboot代码2:stage2代码,启动内核


    一、uboot最终目的:

    1.读出内核 do_nand read kernel
    {
      flash上存的内核:uImage = 头部 + 真正的内核;
    }
    2.启动内核。 do_bootm_linux
    {
      (1)设置启动参数; uboot到kernel的启动参数的传递, 靠的就是告诉kernel参数存放的绝对地址,并按照约定好的格式存放。具体的格式约定比较复杂,见uboot
      (2)跳到入口地址.
    }


    二、在uboot里,打印一下环境变量,下面两句是启动kernel的关键字:

    bootcmd=nand read 0x30000000 0x60000 0x200000;bootm 0x30000000
    bootargs=noinitrd root=/dev/nfs nfsroot=192.168.2.109:/home/fs/work/nfs_root/fs_mini_mdev ip=192.168.2.111:192.168.2.109:192.168.2.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC0

    三、分析uboot代码,看它是如何实现上述功能的:

      写的比较抽象,仅供个人理解。

    //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;
    
    //uboot和kernel传递参数所依赖的结构体,详细内容见 include/asm-arm/setup.h
    struct tag {
        struct tag_header hdr;
        union {
            struct tag_core        core;
            struct tag_mem32    mem;
            struct tag_videotext    videotext;
            struct tag_ramdisk    ramdisk;
            struct tag_initrd    initrd;
            struct tag_serialnr    serialnr;
            struct tag_revision    revision;
            struct tag_videolfb    videolfb;
            struct tag_cmdline    cmdline;
    
            /*
             * Acorn specific
             */
            struct tag_acorn    acorn;
    
            /*
             * DC21285 specific
             */
            struct tag_memclk    memclk;
        } u;
    };
    //cmd_nand.c
    //bootcmd-- nand read 0x30000000 0x60000 0x200000
    int do_nand (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
    {
        nand_read_opts(nand, &opts); // nand_read_opts : drivers/nand/nand_util.c
    }
    
    //cmd_bootm.c
    //bootcmd-- bootm 0x30000000
    int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
    {
        addr = simple_strtoul(argv[1], NULL, 16);
        //printf ("## Booting image at %08lx ...
    ", addr); 
        memmove (&header, (char *)addr, sizeof(image_header_t));
        //puts ("   Verifying Checksum ... ");
        
        //unsigned long int data = start addr of kernel int the sdram after the cmd of nand read;
        if(ntohl(hdr->ih_load) == data) //如果期望的kernel运行地址 = 实际从nand 读入内存的kernel起始地址。(都不含头)
        {// 不需要移动
            printf ("   XIP %s ... ", name);
        } 
        else 
        {// 需要移动
            //printf ("   Loading %s ... ", name);
            len  = ntohl(hdr->ih_size);
            memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
        }
        
        do_bootm_linux (cmdtp, flag, argc, argv,
                 addr, len_ptr, verify)
        {
            void (*theKernel)(int zero, int arch, uint params);
            theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);
            
            setup_start_tag (bd)/设置内核启动参数的其实存放地址bi_boot_params
            {
                bd_t *bd = gd->bd; //gd->bd->bi_boot_params = 0x30000100;
            }
            //这之后从bi_boot_params开始,依次存入各启动参数
            setup_memory_tags (bd); 
            setup_commandline_tag (bd, commandline);//  char *commandline = getenv ("bootargs");
            setup_end_tag (bd);//表示完成启动参数的传递。
            
            theKernel (0, bd->bi_arch_number, bd->bi_boot_params);//跳到入口地址
        }
    }
    
    
    //uboot的核心:通过run_command实现各种功能
    void main_loop (void)
    {
        getenv ("bootdelay");
        
        //uboot如何启动内核:
        s = getenv ("bootcmd");
        printf("Booting Linux ...
    ");            
        run_command (s, 0)
        {
            (cmdtp->cmd) (cmdtp, flag, argc, argv);
        }
        
        //uboot如何接受用户命令:
        for (;;) 
        {
            readline (CFG_PROMPT); //read input into console_buffer
            strcpy (lastcommand, console_buffer);
            run_command (lastcommand, flag);
        }
    
    }
  • 相关阅读:
    固定表头/锁定前几列的代码参考[JS篇]
    盘点mysql中容易被我们误会的地方
    cookie&session的Q&A故事[原理篇]
    网络第一道防线:验证码的故事[安全篇]
    2016,把一年的牛皮先吹了吧[生涯规划篇]
    微软职位内部推荐-Software Engineer II
    微软职位内部推荐-Senior Software Engineer
    微软职位内部推荐-Senior Software Engineer
    微软职位内部推荐-SW Engineer II for Cloud Servi
    微软职位内部推荐-SW Engineer II for Cloud Servi
  • 原文地址:https://www.cnblogs.com/mylinux/p/5043921.html
Copyright © 2020-2023  润新知