引导加载内核
Common/main.c main_loop()
Common/cmd_bootm.c do_bootm()
Lib_arm/armlinux.c do_bootm_linux()
在uboot的stage2的最后阶段,进入main_loop()函数。在这个函数中首先检查是否有启动延时,然后决定是自动加载内核映像来启动内核还是通过命令行来启动内核:
s = getenv ("bootdelay");
bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
上面两行是获得延时时长
s = getenv ("bootcmd");
这行代码是获得启动命令行,这个环境变量的定义是
Bootcmd = "cp 0x200000 0x32000000 0x100000;bootm"
这个在配置头文件include/configs/s3c2410.h中也有定义
#define CONFIG_BOOTCOMMAND "cp 0x200000 0x32000000 0x100000;bootm"
因内核映像在flash的0x200000地址处,所以这里先把内核映像复制到sdram的32000000地址处,然后运行bootm启动。Bootm这个命令将在后面提到
判断延时是否结束,若延时已结束,则运行上面所获得的命令
if (bootdelay >= 0 && s && ! abortboot (bootdelay)) {
# ifdef CONFIG_AUTOBOOT_KEYED
int prev = disable_ctrlc(1); /* disable Control C checking */
# endif
# ifndef CFG_HUSH_PARSER
//运行启动命令
run_command (s, 0);
# else
parse_string_outer(s, FLAG_PARSE_SEMICOLON |
FLAG_EXIT_FROM_LOOP);
# endif
# ifdef CONFIG_AUTOBOOT_KEYED
disable_ctrlc(prev); /* restore Control C checking */
# endif
}
若延时期间按了某个键,即上面粗体字函数 abortboot (bootdelay) 返回1,则进入命令行模式,这时可以通过网络加载内核映像到sdram中:
Tftpboot 32000000 uImage
然后运行bootm启动
或者从flash中拷贝
Cp 200000 32000000 size
然后运行bootm启动。
不管是自动加载,还是在命令行下加载,最后都是把内核映像加载到sdram中,再运行bootm命令启动。下面就说说bootm这个命令。
在cmd_bootm.c中声明并实现了bootm命令
U_BOOT_CMD(
bootm, CFG_MAXARGS, 1, do_bootm,
"bootm - boot application image from memory/n",
"[addr [arg ...]]/n - boot application image stored in memory/n"
" passing arguments 'arg ...'; when booting a Linux kernel,/n"
" 'arg' can be the address of an initrd image/n"
);
若对uboot命令不熟悉可参看http://blog.csdn.net/BoySKung/archive/2008/10/29/3176668.aspx。
其实现函数为do_bootm()
image_header_t header; //存放映像头信息的数据结构
//在include/configs/s3c2410.h中,CFG_LOAD_ADDR=32000000,即内核映像加载在这个地址处
ulong load_addr = CFG_LOAD_ADDR; /* Default Load Address */
int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
ulong iflag;
ulong addr;
ulong data, len, checksum;
ulong *len_ptr;
uint unc_len = 0x400000;
int i, verify;
char *name, *s;
int (*appl)(int, char *[]);
image_header_t *hdr = &header;
//确定是否进行校验
s = getenv ("verify");
//verify = 1时进行校验
verify = (s && (*s == 'n')) ? 0 : 1;
if (argc < 2) {
addr = load_addr; //获得内核映像的起始地址 32000000
} else {
addr = simple_strtoul(argv[1], NULL, 16);
}
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
//将映像头部信息存储在image_header_t 型的变量header中,
memmove (&header, (char *)addr, sizeof(image_header_t));
// printf("header=%x %x %x %x %x %x %x/n", header.ih_magic, header.ih_hcrc, header.ih_time, header.ih_size, header.ih_load, header.ih_ep, header.ih_dcrc);
// printf("header=%x %x %x %x %s/n", header.ih_os, header.ih_arch, header.ih_type, header.ih_comp, header.ih_name);
// image_header_t 结构定义如下
//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;
if (ntohl(hdr->ih_magic) != IH_MAGIC) {
#ifdef __I386__ /* correct image format not implemented yet - fake it */
if (fake_header(hdr, (void*)addr, -1) != NULL) {
/* to compensate for the addition below */
addr -= sizeof(image_header_t);
/* turnof verify,
* fake_header() does not fake the data crc
*/
verify = 0;
} else
#endif /* __I386__ */
{
printf ("Bad Magic Number/n");
SHOW_BOOT_PROGRESS (-1);
return 1;
}
}
SHOW_BOOT_PROGRESS (2);
//获得内核映像头部的起始地址
data = (ulong)&header;
//头部大小
len = sizeof(image_header_t);
//内核映像的crc校验码
checksum = ntohl(hdr->ih_hcrc);
hdr->ih_hcrc = 0;
//检测校验码
if (crc32 (0, (char *)data, len) != checksum) {
printf ("Bad Header Checksum/n");
SHOW_BOOT_PROGRESS (-2);
return 1;
}
SHOW_BOOT_PROGRESS (3);
//打印内核映像头部信息
/* for multi-file images we need the data part, too */
print_image_hdr ((image_header_t *)addr);
//获得映像数据起始地址(除支内核映像头部)
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
//若verify = 1 进行校验
if (verify) {
printf (" Verifying Checksum ... ");
if (crc32 (0l, (volatile char *)data, len) != ntohl(hdr->ih_dcrc)) {
printf ("Bad Data CRC/n");
SHOW_BOOT_PROGRESS (-3);
return 1;
}
printf ("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__)
if (hdr->ih_arch != IH_CPU_MIPS)
#elif defined(__nios__)
if (hdr->ih_arch != IH_CPU_NIOS)
#elif defined(__M68K__)
if (hdr->ih_arch != IH_CPU_M68K)
#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 = 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
//检测映像的压缩类型,用相应工具解压缩
switch (hdr->ih_comp) {
case IH_COMP_NONE:
if(ntohl(hdr->ih_load) == addr) {
printf (" XIP %s ... ", name);
} else {
#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
size_t l = len;
void *to = (void *)ntohl(hdr->ih_load);
void *from = (void *)data;
printf (" Loading %s ... ", name);
while (l > 0) {
size_t tail = (l > CHUNKSZ) ? CHUNKSZ : l;
WATCHDOG_RESET();
memmove (to, from, tail);
to += tail;
from += tail;
l -= tail;
}
#else /* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */
memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
#endif /* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */
}
break;
case IH_COMP_GZIP:
printf (" Uncompressing %s ... ", name);
if (gunzip ((void *)ntohl(hdr->ih_load), unc_len,
(uchar *)data, (int *)&len) != 0) {
printf ("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;
}
printf ("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);
//检测映像是什么操作系统
switch (hdr->ih_os) {
default: /* handled by (original) Linux case */
case IH_OS_LINUX: //为linux,则执行do_bootm_linux,启动linux内核
#ifdef CONFIG_SILENT_CONSOLE
fixup_silent_linux();
#endif
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;
#ifdef CONFIG_LYNXKDI
case IH_OS_LYNXOS:
do_bootm_lynxkdi (cmdtp, flag, argc, argv,
addr, len_ptr, verify);
break;
#endif
case IH_OS_RTEMS:
do_bootm_rtems (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 */
#ifdef CONFIG_ARTOS
case IH_OS_ARTOS:
do_bootm_artos (cmdtp, flag, argc, argv,
addr, len_ptr, verify);
break;
#endif
}
SHOW_BOOT_PROGRESS (-9);
#ifdef DEBUG
printf ("/n## Control returned to monitor - resetting.../n");
do_reset (cmdtp, flag, argc, argv);
#endif
return 1;
}
当根据头信息检测到是linux内核映像是,执行do_bootm_linux启动内核 lib_arm/armlinux.c
void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
ulong addr, ulong *len_ptr, int verify)
{
DECLARE_GLOBAL_DATA_PTR;
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
//初始化内核entry point的地址
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);
/*
* Check if there is an initrd image
*/
//检查有没有initrd 映像
//若有,则载到内存,并进行头部和数据的crc校验
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, (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
memcpy ((char *)CONFIG_LOAD_INITRD_FILE_ADDR, (char *)data, len);
data = CONFIG_LOAD_INITRD_FILE_ADDR;
if (verify) {
ulong csum = 0;
printf (" Verifying Checksum ... ");
csum = crc32 (0, (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);
}
#ifdef CONFIG_B2
/*
*we need to copy the ramdisk to SRAM to let Linux boot
*/
memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
data = ntohl(hdr->ih_load);
#endif /* CONFIG_B2 */
/*
* 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*/printf("## 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_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);
#endif
#ifdef CONFIG_INITRD_TAG
if (initrd_start && initrd_end)
setup_initrd_tag (bd, initrd_start, initrd_end);
#endif
#if defined (CONFIG_VFD)
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");
//内核启动前对cpu进行设置
cleanup_before_linux ();
//跳转到内核的入口点开始执行,所传递的三个参数分别存放在
//r0(0); r1(machin type number); r2(physical address of tagged list in system RAM);
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
}
到这里,控制权交到了内核的手中
总结:do_bootm主要是将内核解到内核映像头部信息指定的加载地址中,do_bootm_linux主要是设置参数,并跳转到内核入口开始执行。