uboot之do_bootm_linux启动内核函数源码解析

当配置了CONFIG_PPC时将调用common/cmd_bootm.c中的do_bootm_linux。本处是调用libarm/armlinux.c中的。
u-boot.h中
static struct tag params;
typedef struct bd_info {
int bi_baudrate; /
serial console baudrate /波特率
unsigned long bi_ip_addr; /
IP Address /即服务器IP地址
unsigned char bi_enetaddr[6]; /
Ethernet adress */
struct environment_s bi_env;
//开发板机器ID,即1008(MACH_TYPE_SMDK2440),gd->bd->bi_arch_number = MACH_TYPE_SMDK2440; (smdk2440.c)
ulong bi_arch_number; /
unique id for this board /
//准确地说,这是启动参数的地址,gd->bd->bi_boot_params =0x30000100;(smdk2440.c),与内核中需要一致。
ulong bi_boot_params; /
where this board expects params /准确地说,这是启动参数的地址
struct /
RAM configuration /
{
ulong start;//内存起始地址
ulong size;//内存大小
} bi_dram[CONFIG_NR_DRAM_BANKS];
#ifdef CONFIG_HAS_ETH1
/
second onboard ethernet port */
unsigned char bi_enet1addr[6];
#endif
} bd_t;
在u-boot下输入bd就可以查看开发板的一些信息
uplooking # bd
arch_number = 0x0000065A
env_t = 0x00000000
boot_params = 0x50000100
DRAM bank = 0x00000000
-> start = 0x50000000
-> size = 0x08000000
ethaddr = 00:40:5C:26:0A:5B
ip_addr = 192.168.1.20
baudrate = 115200 bps

///
command.h
/*

  • Monitor Command Table
    */

struct cmd_tbl_s {
char name; / Command Name //命令名/
int maxargs; /
maximum number of arguments //命令的最大参数个数/
int repeatable; /
autorepeat allowed? //是否自动重复/
/
Implementation function */
int (*cmd)(struct cmd_tbl_s *, int, int, char *[]);/命令执行函数/
char usage; / Usage message (short) */ /简单的使用说明/
#ifdef CFG_LONGHELP
char help; / Help message (long) / /详细使用说明/
#endif
#ifdef CONFIG_AUTO_COMPLETE /自动补全参数/
/
do auto completion on the arguments */
int (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
#endif
};
在文件include/command.h中定义了结构体cmd_tbl_s,各成员含义如上面注释。U-boot的每一条命令都将封装成结构体 cmd_tbl_s存到链接文件中指定的.u_boot_cmd区。当向u-boot中添加命令时都将调用宏 U_BOOT_CMD(name,maxargs,rep,cmd,usage,help)来初始化一个cmd_tbl_s 结构体。
typedef struct cmd_tbl_s cmd_tbl_t;

当定义了CONFIG_PPC时将使用common/cmd_bootm.c文件中的do_bootm_linux函数;当系统中没有定义该宏时,系统将使用lib_arm/armlinux.c文件中定义的do_bootm_linux函数。注意:这两个函数有很大的区别

void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
ulong addr, ulong *len_ptr, int verify)
{
ulong len = 0, checksum;
ulong initrd_start, initrd_end;
ulong data;
void (theKernel)(int zero, int arch, uint params);
image_header_t hdr = &header;
bd_t bd = gd->bd;
#ifdef CONFIG_CMDLINE_TAG
char commandline = getenv (“bootargs”);//调用了getenv将bootargs环境变量保存在commandline
#endif
//uboot set参数命令行bootm 50008000;
//打印出来发现 hdr->ih_ep = 0x800050 也就是必须ntohl之后才是50008000
theKernel = (void (
)(int, int, uint))ntohl(hdr->ih_ep);
//打印出来发现theKernel == 0x50008000
//设置内核入口地址(不是加载地址,加载是程序存储地址),入口地址是PC指针,倒是后程序跳到这个地址运行
//mkimage.c
//#define ntohl(a) SWAP_LONG(a)
//#define htonl(a) SWAP_LONG(a)
/

#define SWAP_LONG(x)
((__u32)(
(((__u32)(x) & (__u32)0x000000ffUL) << 24) |
(((__u32)(x) & (__u32)0x0000ff00UL) << 8) |
(((__u32)(x) & (__u32)0x00ff0000UL) >> 8) |
(((__u32)(x) & (__u32)0xff000000UL) >> 24) ))
***/
/

  • Check if there is an initrd image //跳过 不是initrd镜像
    /
    if (argc >= 3) {
    addr = simple_strtoul (argv[2], NULL, 16);
    printf (“## Loading Ramdisk Image at %08lx …\n”, addr);
    /
    Copy header so we can blank CRC field for re-calculation */
    #ifdef CONFIG_HAS_DATAFLASH
    if (addr_dataflash (addr)) {
    read_dataflash (addr, sizeof (image_header_t), (char *) &header);
    }
    else
    #endif
    memcpy (&header, (char *) addr, sizeof (image_header_t));
    if (ntohl (hdr->ih_magic) != IH_MAGIC) {
    printf (“Bad Magic Number\n”);
    do_reset (cmdtp, flag, argc, argv);
    }
    data = (ulong) & header;
    len = sizeof (image_header_t);
    checksum = ntohl (hdr->ih_hcrc);
    hdr->ih_hcrc = 0;
    if (crc32 (0, (unsigned char ) data, len) != checksum) {
    printf (“Bad Header Checksum\n”);
    do_reset (cmdtp, flag, argc, argv);
    }
    print_image_hdr (hdr);
    data = addr + sizeof (image_header_t);
    len = ntohl (hdr->ih_size);
    #ifdef CONFIG_HAS_DATAFLASH
    if (addr_dataflash (addr)) {
    read_dataflash (data, len, (char ) CFG_LOAD_ADDR);
    data = CFG_LOAD_ADDR;
    }
    #endif
    /

    **do_bootm函数中
    ** s = getenv (“verify”);
    verify = (s && (*s == ‘n’)) ? 0 : 1;
    //为是否对镜像头做校验做准备,读取uboot的环境变量verify,
    如果环境变量verify等于’n’,则局部变量verify赋值成为0;如果环境变量verify为空(即没有
    定义环境变量verify)或者环境变量verify不等于’n’,则局部变量verify赋值成为1。
    **
    **/
    if (verify) {
    ulong csum = 0;
    printf (" Verifying Checksum … ");
    csum = crc32 (0, (unsigned char ) data, len);
    if (csum != ntohl (hdr->ih_dcrc)) {
    printf (“Bad Data CRC\n”);
    do_reset (cmdtp, flag, argc, argv);
    }
    printf (“OK\n”);
    }
    if ((hdr->ih_os != IH_OS_LINUX) || (hdr->ih_arch != IH_CPU_ARM) || (hdr->ih_type != IH_TYPE_RAMDISK)) {
    printf (“No Linux ARM Ramdisk Image\n”);
    do_reset (cmdtp, flag, argc, argv);
    }
    #if defined(CONFIG_B2) || defined(CONFIG_EVB4510) || defined(CONFIG_ARMADILLO)
    /

    *we need to copy the ramdisk to SRAM to let Linux boot
    */
    memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);//与上文的memcpy (&header, (char ) addr, sizeof (image_header_t));区别??
    data = ntohl(hdr->ih_load);
    #endif /
    CONFIG_B2 || CONFIG_EVB4510 /
    /
  • Now check if we have a multifile image
    /
    } //end of " if (argc >= 3) {"
    else if ((hdr->ih_type == IH_TYPE_MULTI) && (len_ptr[1])) {
    ulong tail = ntohl (len_ptr[0]) % 4;
    int i;
    /
    skip kernel length and terminator /
    data = (ulong) (&len_ptr[2]);
    /
    skip any additional image length fields /
    for (i = 1; len_ptr[i]; ++i)
    data += 4;
    /
    add kernel length, and align /
    data += ntohl (len_ptr[0]);
    if (tail) {
    data += 4 - tail;
    }
    len = ntohl (len_ptr[1]);
    }
    else {
    /
  • no initrd image
    */
    len = data = 0;
    }
    #ifdef DEBUG
    if (!data) {
    printf (“No initrd\n”);
    }
    #endif
    if (data) {
    initrd_start = data;
    initrd_end = initrd_start + len;
    } else {
    initrd_start = 0;
    initrd_end = 0;
    }
    debug (“## Transferring control to Linux (at address %08lx) …\n”,
    (ulong) theKernel);
    #if defined (CONFIG_SETUP_MEMORY_TAGS) ||
    defined (CONFIG_CMDLINE_TAG) ||
    defined (CONFIG_INITRD_TAG) ||
    defined (CONFIG_SERIAL_TAG) ||
    defined (CONFIG_REVISION_TAG) ||
    defined (CONFIG_LCD) ||
    defined (CONFIG_VFD)
    setup_start_tag (bd);
    #ifdef CONFIG_SERIAL_TAG
    setup_serial_tag (?ms);
    #endif
    #ifdef CONFIG_REVISION_TAG
    setup_revision_tag (?ms);
    #endif
    #ifdef CONFIG_SETUP_MEMORY_TAGS
    setup_memory_tags (bd);
    #endif
    #ifdef CONFIG_CMDLINE_TAG
    setup_commandline_tag (bd, commandline); //设置启动命令行tags
    #endif
    #ifdef CONFIG_INITRD_TAG
    if (initrd_start && initrd_end)
    setup_initrd_tag (bd, initrd_start, initrd_end);
    #endif
    #if defined (CONFIG_VFD) || defined (CONFIG_LCD)
    setup_videolfb_tag ((gd_t ) gd);
    #endif
    setup_end_tag (bd);
    #endif
    /
    we assume that the kernel is in place */
    printf (“\nStarting kernel …\n\n”);
    #ifdef CONFIG_USB_DEVICE
    {
    extern void udc_disconnect (void);
    udc_disconnect ();
    }
    #endif
    cleanup_before_linux ();
    //传递的参数
    //R0必须为0 ????
    //R1:机器类型ID ,本机为ARM(bd-> bi_arch_number)
    //R2:启动参数列表在内存中的位置(bd-> bi_boot_params)
    theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
    //<嵌入式linux应用开发>P264 bd->bi_boot_params就是标记列表的开始地址。
    }

///image.h
/*

  • all data in network byte order (aka natural aka bigendian)
    /
    typedef struct image_header {
    uint32_t ih_magic; /
    Image Header Magic Number / /镜像幻数/
    uint32_t ih_hcrc; /
    Image Header CRC Checksum / / 镜像头CRC校验值*/
    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 / //theKernel函数指针指向这个地址,也就是从uboot跳到内核的地址, 镜像入口*/
    uint32_t ih_dcrc; /* Image Data CRC Checksum / / 镜像数据区的CRC校验值*/
    uint8_t ih_os; /* Operating System // 操作系统类型*/
    uint8_t ih_arch; /* CPU architecture // CPU架构*/
    uint8_t ih_type; /* Image Type / / 镜像类型*/
    uint8_t ih_comp; /* Compression Type / / 压缩类型*/
    uint8_t ih_name[IH_NMLEN]; /* Image Name // 镜像名*/
    } image_header_t;

Linux编译出的二进制文件是zImage,uboot中提供mkimage工具可将zImage制作成uImage,实际上就是在zImage前加一个image_header头。制作uImage的命令如下。

./mkimage -n ‘linux-2.6.36’ -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008000 -d zImage uImage.bin

Mkimage工具参数的含义如下:

-A ==> 设置体系架构类型

-O ==> 设置操作系统类型

-T ==> 设置镜像类型

-C ==> 设置压缩类型

-a ==> 设置加载地址

-e ==> 设置镜像入口位置

-n ==> 设置镜像名

-d ==> 设置生成最终镜像所需的文件

-x ==> 设置片上执行

  • 9
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xx-xx-xxx-xxx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值