uboot引导linux内核,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) <

(((__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);

//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 ==> 设置片上执行

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值