当配置了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 ==> 设置片上执行