/////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////
2.假设bootcmd = nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0
<1> nand read.jffs2 0x30007FC0 kernel
nand read.jffs2 0x30007FC0 kernel;
从nand读出内核:从哪里读? 从kernel分区
放到哪里去?-0x30007FC0
其实我们把内核中nand读出来的时候是可以放在内核的任何地方的,如0x31000000,0x32000000等等,只要它不破坏uboot所占用的内存空间就可以了,如下图:
从0x33F4DF74-0x30000000(这是哪个CPU芯片的内存?)都是可以用的。
我们所用的内核加载地址是0x50008000,而头部的大小为64个字节,所以加载头部的64个字节后,内核正好位于0x50008040处!
此处是
#./mkimage -n
//#tftp 50003000 uImage //uboot烧录uImage到内存50003000
//#nand erase 40000 400000
//#nand write 50003000 40000 400000 //将内存50003000开始的东西烧录到40000 nandflash地址
//#set bootcmd "nand read 50008000 40000 400000;bootm 50008000
//#./mkimage -n 'linux-3.2.1' -A arm -O linux -T kernel -C none -a 0x50008000 -e 0x50008040 -d zImage uImage
///////////////////////////////////////////////////////////////////////////////////
common/cmd_bootm.c
ulong load_addr = CFG_LOAD_ADDR; /* Default Load Address */
//cmd_tbl_t 是描述每一个命令的结构体
int
do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
///bootm 50008000命令
ulong iflag;
ulong addr;
ulong data, len, checksum;
ulong *len_ptr = NULL; /* not to make warning. by scsuh */
uint unc_len = CFG_BOOTM_LEN;
int i, verify;
char *name, *s;
int (*appl)(int, char *[]);
image_header_t *hdr = &header;
s = getenv ("verify");
verify = (s && (*s == 'n')) ? 0 : 1;
if (argc < 2) {
addr = load_addr;
} else {
addr = simple_strtoul(argv[1], NULL, 16); //bootm 50001000
}
//#define LINUX_ZIMAGE_MAGIC 0x016f2818
//(1)这是一个魔数,0x016f2818表示这个镜像是zImage,
//也就是说zImage格式的镜像中,在头部的一个固定位置存放了这个数,作为格式标记,
//如果我们拿到了一个image,去他的那个位置去取4个字节,判断它是否等于这个魔数LINUX_ZIMAGE_MAGIC。则可以知道这个镜像是不是zImage
#ifdef CONFIG_ZIMAGE_BOOT
#define
LINUX_ZIMAGE_MAGIC 0x016f2818
if (*(ulong *)(addr + 9*4) == LINUX_ZIMAGE_MAGIC) {
////zImage从头部开始的第37到40个字节,存的是zImage的标志的魔数
printf("Boot with zImage\n");
//addr == 50008000
addr = virt_to_phys(addr);
//virt_to_phys之后还是50008000
hdr->ih_os = IH_OS_LINUX;
hdr->ih_ep = ntohl(addr);
goto
after_header_check;//一下子跳了过去,中间一大段memmove的东东都不执行了。如果烧录的是zImage
}
#endif
SHOW_BOOT_PROGRESS (1);
printf ("## Booting 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
//获得uimage 头部信息?
////读取kernel image的header
//从镜像内存首地址读取镜像头部,为下面的分析校验做准备
//判断魔数,一般不会出错
memmove (&header, (char *)addr, sizeof(image_header_t));
if (ntohl(hdr->ih_magic) != IH_MAGIC) {
{
////如果是引导uImage镜像,不会进入这里
#ifdef CONFIG_IMAGE_BOOT
printf("Boot with Image\n");
addr = virt_to_phys(addr);
hdr->ih_os = IH_OS_LINUX;
hdr->ih_ep = ntohl(addr);
hdr->ih_comp = IH_COMP_NONE;
goto after_header_check;
#endif
puts ("Bad Magic Number\n");
SHOW_BOOT_PROGRESS (-1);
return 1;
}
}
SHOW_BOOT_PROGRESS (2);
data = (ulong)&header;
len = sizeof(image_header_t);
checksum = ntohl(hdr->ih_hcrc);//对镜像头做crc校验
hdr->ih_hcrc = 0;
if (crc32 (0, (uchar *)data, len) != checksum) {
puts ("Bad Header Checksum\n");
SHOW_BOOT_PROGRESS (-2);
return 1;
}
SHOW_BOOT_PROGRESS (3);
#ifdef CONFIG_HAS_DATAFLASH
if (addr_dataflash(addr)){
len = ntohl(hdr->ih_size) + sizeof(image_header_t);
read_dataflash(addr, len, (char *)CFG_LOAD_ADDR);
addr = CFG_LOAD_ADDR;
}
#endif
/* for multi-file images we need the data part, too */
print_image_hdr ((image_header_t *)addr);
data = addr + sizeof(image_header_t);
//指向后面的kernel部分 也就是镜像文件uImage的zImage部分
len = ntohl(hdr->ih_size);
////kernel的实际大小,就是编译后的大小
if (verify) {//对镜像的数据部分做crc校验
puts (" Verifying Checksum ... ");
if (crc32 (0, (uchar *)data, len) != ntohl(hdr->ih_dcrc)) {
printf ("Bad Data CRC\n");
SHOW_BOOT_PROGRESS (-3);
return 1;
}
puts ("OK\n");
}
SHOW_BOOT_PROGRESS (4);
len_ptr = (ulong *)data;
#if defined(__PPC__)
if (hdr->ih_arch != IH_CPU_PPC)
#elif defined(__ARM__)
if (hdr->ih_arch != IH_CPU_ARM)
#elif defined(__I386__)
if (hdr->ih_arch != IH_CPU_I386)
#elif defined(__mips__)此处有省略
#else
# error Unknown CPU type
#endif
{
printf ("Unsupported Architecture 0x%x\n", hdr->ih_arch);
SHOW_BOOT_PROGRESS (-4);
return 1;
}
SHOW_BOOT_PROGRESS (5);
switch (hdr->ih_type) {
case IH_TYPE_STANDALONE:
name = "Standalone Application";
/* A second argument overwrites the load address */
if (argc > 2) {
hdr->ih_load = htonl(simple_strtoul(argv[2], NULL, 16));
}
break;
case IH_TYPE_KERNEL:
name = "Kernel Image";
break;
case IH_TYPE_MULTI:
name = "Multi-File Image";
len = ntohl(len_ptr[0]);
/* OS kernel is always the first image */
data += 8; /* kernel_len + terminator */
for (i=1; len_ptr[i]; ++i)
data += 4;
break;
default: printf ("Wrong Image Type for %s command\n", cmdtp->name);
SHOW_BOOT_PROGRESS (-5);
return 1;
}
SHOW_BOOT_PROGRESS (6);
/*
* We have reached the point of no return: we are going to
* overwrite all exception vector code, so we cannot easily
* recover from any failures any more...
*/
iflag = disable_interrupts();
#ifdef CONFIG_AMIGAONEG3SE
/*
* We've possible left the caches enabled during
* bios emulation, so turn them off again
*/
icache_disable();
invalidate_l1_instruction_cache();
flush_data_cache();
dcache_disable();
#endif
/* 最关键的地方: 搬运kernel到ih_load指定的地址上去*/
//根据镜像的压缩类型把内核镜像解压到指定的地址,一般是mkimage时-a指定的
那个地址,-a指定的那个地址也就是存储在uImage镜像头里面的hdr->ih_load变量中,
switch (hdr->ih_comp) {
case IH_COMP_NONE:
/// -C none
if(ntohl(hdr->ih_load) == addr) {
//如果image header中指示的加载地址和bootm命令中参数2指定的地址相同,则表示不需要copy,可以就地(到hdr->ih_ep)执行代码。
//此处是bootm 50008000;这个bootm的地址(addr)与( uImage里的hdr->ih_load加载 地址相同) ,所以不memmove代码到ih_load加载地址
//
所以hdr->ih_ep必须为50008040 = ih_load(50008000,也就是bootm命令设置的地址)+uImage头部64(0x40)字节
//#tftp 50003000 uImage //uboot烧录uImage到内存50003000
//#nand erase 40000 400000
//#nand write 50003000 40000 400000 //将内存50003000开始的东西烧录到40000 nandflash地址
//#set bootcmd "nand read 50008000 40000 400000;bootm 50008000
//#./mkimage -n 'linux-3.2.1' -A arm -O linux -T kernel -C none -a 0x50008000 -e 0x50008040 -d zImage uImage
printf (" XIP %s ... ", name);
} else {
//bootm地址和uImage头部的hdr->ih_load地址不同
//这里就是搬运的代码
// armlinux.c 在do_bootm_linux函数 theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep); 是跳到入口地址运行hdr->ih_ep
//而uImage 后面的数据(也就是zImage部分)被memmove到了加载地址hdr->ih_load
//所以这种情况是制作镜像uImage时候入口地址等于加载地址 hdr->ih_load == hdr->ih_ep (smdk6410是50008000)
//#set bootcmd "nand read 51000000 40000 400000;bootm 51000000" uboot 里的bootm地址不能是 hdr->ih_load (50008000)加载地址
memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
}
break;
case IH_COMP_GZIP:
printf (" Uncompressing %s ... ", name);
if (gunzip ((void *)ntohl(hdr->ih_load), unc_len,
(uchar *)data, &len) != 0) {
// 把它解压到 ih_load的位置上去
puts ("GUNZIP ERROR - must RESET board to recover\n");
SHOW_BOOT_PROGRESS (-6);
do_reset (cmdtp, flag, argc, argv);
}
break;
#ifdef CONFIG_BZIP2
case IH_COMP_BZIP2:
printf (" Uncompressing %s ... ", name);
/*
* If we've got less than 4 MB of malloc() space,
* use slower decompression algorithm which requires
* at most 2300 KB of memory.
*/
i = BZ2_bzBuffToBuffDecompress ((char*)ntohl(hdr->ih_load),
&unc_len, (char *)data, len,
CFG_MALLOC_LEN < (4096 * 1024), 0);
if (i != BZ_OK) {
printf ("BUNZIP2 ERROR %d - must RESET board to recover\n", i);
SHOW_BOOT_PROGRESS (-6);
udelay(100000);
do_reset (cmdtp, flag, argc, argv);
}
break;
#endif /* CONFIG_BZIP2 */
default:
if (iflag)
enable_interrupts();
printf ("Unimplemented compression type %d\n", hdr->ih_comp);
SHOW_BOOT_PROGRESS (-7);
return 1;
}
//搬运完毕
puts ("OK\n");
SHOW_BOOT_PROGRESS (7);
switch (hdr->ih_type) {
case IH_TYPE_STANDALONE:
if (iflag)
enable_interrupts();
/* load (and uncompress), but don't start if "autostart"
* is set to "no"
*/
if (((s = getenv("autostart")) != NULL) && (strcmp(s,"no") == 0)) {
char buf[32];
sprintf(buf, "%lX", len);
setenv("filesize", buf);
return 0;
}
appl = (int (*)(int, char *[]))ntohl(hdr->ih_ep);
(*appl)(argc-1, &argv[1]);
return 0;
case IH_TYPE_KERNEL:
case IH_TYPE_MULTI:
/* handled below */
break;
default:
if (iflag)
enable_interrupts();
printf ("Can't boot image type %d\n", hdr->ih_type);
SHOW_BOOT_PROGRESS (-8);
return 1;
}
SHOW_BOOT_PROGRESS (8);
#if defined(CONFIG_ZIMAGE_BOOT) || defined(CONFIG_IMAGE_BOOT)
after_header_check:
#endif
switch (hdr->ih_os) {
default: /* handled by (original) Linux case */
case IH_OS_LINUX:
#ifdef CONFIG_SILENT_CONSOLE
fixup_silent_linux();
#endif
//接下来就要调用do_bootm_linux() 函数了,这里要启动kernel
do_bootm_linux (cmdtp, flag, argc, argv,
addr, len_ptr, verify);
break;
case IH_OS_NETBSD:
do_bootm_netbsd (cmdtp, flag, argc, argv,
addr, len_ptr, verify);
break;
#if (CONFIG_COMMANDS & CFG_CMD_ELF)
case IH_OS_VXWORKS:
do_bootm_vxworks (cmdtp, flag, argc, argv,
addr, len_ptr, verify);
break;
case IH_OS_QNX:
do_bootm_qnxelf (cmdtp, flag, argc, argv,
addr, len_ptr, verify);
break;
#endif /* CFG_CMD_ELF */
}
SHOW_BOOT_PROGRESS (-9);
#ifdef DEBUG
puts ("\n## Control returned to monitor - resetting...\n");
do_reset (cmdtp, flag, argc, argv);
#endif
return 1;
}
///////////////////////////////////////////////////////////////////////////////////////
libarm/armlinux.c
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");
#endif
//uboot set参数命令行bootm 50008000;
//打印出来发现 hdr->ih_ep = 0x800050 也就是必须ntohl之后才是50008000
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);
//此跳到了内核去执行 设置入口地址为函数theKernel的地址,PC指针倒是后是入口地址,也就跳到入口地址去执行程序了。
//打印出来发现theKernel == 0x50008000
//设置内核入口地址(不是加载地址,加载是程序存储地址),入口地址是PC指针,倒是后程序跳到这个地址运行
/*
* Check if there is an initrd image 我们的启动参数里就是 noinitrd 此处不执行,参数也没有大于等于3
*/
if (argc >= 3) {
SHOW_BOOT_PROGRESS (9);
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");
SHOW_BOOT_PROGRESS (-10);
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");
SHOW_BOOT_PROGRESS (-11);
do_reset (cmdtp, flag, argc, argv);
}
SHOW_BOOT_PROGRESS (10);
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
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");
SHOW_BOOT_PROGRESS (-12);
do_reset (cmdtp, flag, argc, argv);
}
printf ("OK\n");
}
SHOW_BOOT_PROGRESS (11);
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");
SHOW_BOOT_PROGRESS (-13);
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
*/
//与上文的memcpy (&header, (char *) addr, sizeof (image_header_t));区别??
memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
data = ntohl(hdr->ih_load);
#endif /* CONFIG_B2 || CONFIG_EVB4510 */
/*
* Now check if we have a multifile image
*/
} else if ((hdr->ih_type == IH_TYPE_MULTI) && (len_ptr[1])) {
ulong tail = ntohl (len_ptr[0]) % 4;
int i;
SHOW_BOOT_PROGRESS (13);
/* 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
*/
SHOW_BOOT_PROGRESS (14);
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;
}
SHOW_BOOT_PROGRESS (15);
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就是标记列表的开始地址。
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
}