uboot启动内核时候依赖于两条代码:
s=getev("bootcmd")
run_command(s,?)
s=nand read.jffs2 0x30007fc0 kernel;
从nandflash读出内核,
从哪里读?从kernel分区读,pc的每块硬盘的开始多有该硬盘的分区表,而nandflash是没有分区表的,但是开发人员头脑中要有一个存储器大致划分概念
|uboot分区|uboot的环境变量|kernel分区|root分区也即文件系统|,既然没有分区表那么存储区域的划分只能是在源代码中固定。所以我们关心的是各个分区的起始地址。例如韦东山是在100ask24x0.h文件中用一个宏定义,#define
MTDPARTS_DEFAULT
"mtdparts=nandflash0:256K@0(bootloader),"\也即从0到256K为bootloader
"128k(params),"\接下来的128K放的是uboot的环境变量
"2m(kernel),"\ 2M空间放的是kernel
"-(root)" 剩下的是root分区
读到哪里去?-----0x30007fc0
具体地址从uboot菜单中输入mtd命令即可。下面的这个图是我的四个分区:
所以 nand read.jffs2 0x30007fc0 kernel = nand read.jffs2 0x30007fc0
0x00060000 0x0x00200000
下面分析一下如何读,如何把2M的内核读到0x30007fc0处????
因为启动时do_bootm,所以可以猜测nand read 应该是do_nand函数。
---------------------------do_nand函数开始----------------------------
int do_nand(cmd_tbl_t * cmdtp, int flag, int
argc, char *argv[])
{
int i, dev, ret;
ulong addr, off, size;
char *cmd, *s;
nand_info_t *nand;
int quiet = 0;
const char *quiet_str = getenv("quiet");
if (argc < 2)
goto usage;
if (quiet_str)
quiet =
simple_strtoul(quiet_str, NULL, 0) != 0;
cmd = argv[1];
if (strcmp(cmd, "info") == 0) {
putc('\n');
for (i = 0; i <
CFG_MAX_NAND_DEVICE; i++) {
if
(nand_info[i].name)
printf("Device
%d: %s, sector size %lu KiB\n",
i,
nand_info[i].name,
nand_info[i].erasesize
>> 10);
}
return 0;
}
if (strcmp(cmd, "device") == 0) {
if (argc < 3)
{
if
((nand_curr_device < 0) ||
(nand_curr_device >= CFG_MAX_NAND_DEVICE))
puts("\nno
devices available\n");
else
printf("\nDevice
%d: %s\n", nand_curr_device,
nand_info[nand_curr_device].name);
return
0;
}
dev =
(int)simple_strtoul(argv[2], NULL, 10);
if (dev < 0 ||
dev >= CFG_MAX_NAND_DEVICE || !nand_info[dev].name)
{
puts("No such
device\n");
return
1;
}
printf("Device %d: %s", dev,
nand_info[dev].name);
puts("... is now current
device\n");
nand_curr_device = dev;
#ifdef CFG_NAND_SELECT_DEVICE
board_nand_select_device(nand_info[dev].priv,
dev);
#endif
return 0;
}
if (strcmp(cmd, "bad") != 0
&& strcmp(cmd, "erase") != 0
&&
strncmp(cmd, "dump", 4) != 0
&&
strncmp(cmd, "read", 4) != 0 &&
strncmp(cmd, "write", 5) != 0
&&
strcmp(cmd, "scrub") != 0 &&
strcmp(cmd, "markbad") != 0
&&
strcmp(cmd, "biterr") != 0 &&
strcmp(cmd, "lock") != 0 &&
strcmp(cmd, "unlock") != 0 )
goto usage;
if (nand_curr_device < 0 ||
nand_curr_device >= CFG_MAX_NAND_DEVICE ||
!nand_info[nand_curr_device].name) {
puts("\nno devices
available\n");
return 1;
}
nand =
&nand_info[nand_curr_device];
if (strcmp(cmd, "bad") == 0) {
printf("\nDevice %d bad
blocks:\n", nand_curr_device);
for (off = 0; off
< nand->size; off +=
nand->erasesize)
if
(nand_block_isbad(nand, off))
printf(" x\n", off);
return 0;
}
if (strcmp(cmd, "erase") == 0 || strcmp(cmd,
"scrub") == 0) {
nand_erase_options_t
opts;
int clean = argc
> 2 &&
!strcmp("clean", argv[2]);
int o = clean ? 3 : 2;
int scrub = !strcmp(cmd,
"scrub");
printf("\nNAND %s: ", scrub
? "scrub" : "erase");
if (arg_off_size(argc - o, argv
+ o, nand, &off, &size) != 0)
return 1;
memset(&opts,
0, sizeof(opts));
opts.offset = off;
opts.length = size;
opts.jffs2 =
clean;
opts.quiet =
quiet;
if (scrub) {
puts("Warning:
"
"scrub option will erase all factory set "
"bad blocks!\n"
" "
"There is no reliable way to recover them.\n"
" "
"Use this command only for testing purposes "
"if you\n"
" "
"are sure of what you are doing!\n"
"\nReally scrub this NAND flash?
\n");
if (getc()
== 'y' && getc() == '\r') {
opts.scrub
= 1;
} else
{
puts("scrub
aborted\n");
return
-1;
}
}
ret = nand_erase_opts(nand,
&opts);
printf("%s\n", ret ? "ERROR" :
"OK");
return ret == 0 ? 0 :
1;
}
if (strncmp(cmd, "dump", 4) == 0)
{这个是用来显示nandflash的数据的
if (argc <
3)
goto
usage;
s = strchr(cmd, '.');
off =
(int)simple_strtoul(argv[2], NULL, 16);
if (s != NULL
&& strcmp(s, ".oob") == 0)
ret =
nand_dump_oob(nand, off);
else
ret =
nand_dump(nand, off);
return ret == 0 ? 1 : 0;
}
if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd,
"write", 5) == 0) {
int read;
if (argc <
4)
goto
usage;
addr =
(ulong)simple_strtoul(argv[2], NULL, 16);
read = strncmp(cmd, "read",
4) == 0;
printf("\nNAND %s: ", read ?
"read" : "write");
if (arg_off_size(argc - 3, argv
+ 3, nand, &off, &size) != 0)
return 1;
s = strchr(cmd, '.');
if (s != NULL
&&
(!strcmp(s, ".jffs2") || !strcmp(s, ".e") || !strcmp(s, ".i")))
{这里用jffs2文件格式,其实没有牵扯到文件格式,但是用这个的话不用考虑页或者块对齐,其他格式得考虑。
if (read)
{
nand_read_options_t
opts;
memset(&opts,
0, sizeof(opts));
opts.buffer =
(u_char*) addr;
opts.length =
size;
opts.offset =
off;
opts.quiet = quiet;
ret
= nand_read_opts(nand,
&opts);最终调用这个函数具体实现先不看。
} else
{
nand_write_options_t
opts;
memset(&opts,
0, sizeof(opts));
opts.buffer =
(u_char*) addr;
opts.length =
size;
opts.offset =
off;
opts.pad =
1;
opts.blockalign
= 1;
opts.quiet = quiet;
ret
= nand_write_opts(nand, &opts);
}
} else {
if
(read)
ret
= nand_read(nand, off, &size, (u_char
*)addr);
else
ret
= nand_write(nand, off, &size, (u_char
*)addr);
}
printf(" %d bytes %s: %s\n",
size,
read ? "read" : "written", ret ? "ERROR" : "OK");
return ret == 0 ? 0 :
1;
}
if (strcmp(cmd, "markbad") == 0) {
addr =
(ulong)simple_strtoul(argv[2], NULL, 16);
int ret =
nand->block_markbad(nand, addr);
if (ret == 0) {
printf("block
0xlx successfully marked as bad\n",
(ulong) addr);
return
0;
} else {
printf("block
0xlx NOT marked as bad! ERROR %d\n",
(ulong) addr, ret);
}
return 1;
}
if (strcmp(cmd, "biterr") == 0) {
return 1;
}
if (strcmp(cmd, "lock") == 0) {
int tight =
0;
int status = 0;
if (argc == 3) {
if
(!strcmp("tight", argv[2]))
tight
= 1;
if
(!strcmp("status", argv[2]))
status
= 1;
}
if (status) {
ulong
block_start = 0;
ulong
off;
int
last_status = -1;
struct
nand_chip *nand_chip = nand->priv;
nand_chip->cmdfunc
(nand, NAND_CMD_STATUS, -1, -1);
printf("device
is %swrite protected\n",
(nand_chip->read_byte(nand) & 0x80
?
"NOT
" : "" ) );
for (off =
0; off < nand->size; off +=
nand->oobblock) {
int
s = nand_get_lock_status(nand, off);
if
(off == nand->size -
nand->oobblock
|| (s != last_status && off !=
0)) {
printf("x
- x: � pages %s%s%s\n",
block_start,
off-1,
(off-block_start)/nand->oobblock,
((last_status & NAND_LOCK_STATUS_TIGHT) ? "TIGHT "
: ""),
((last_status & NAND_LOCK_STATUS_LOCK) ? "LOCK " :
""),
((last_status & NAND_LOCK_STATUS_UNLOCK) ? "UNLOCK
" : ""));
}
last_status
= s;
}
} else {
if
(!nand_lock(nand, tight)) {
puts("NAND
flash successfully locked\n");
} else
{
puts("Error
locking NAND flash\n");
return
1;
}
}
return 0;
}
if (strcmp(cmd, "unlock") == 0) {
if (arg_off_size(argc - 2, argv
+ 2, nand, &off, &size)
< 0)
return 1;
if (!nand_unlock(nand, off,
size)) {
puts("NAND
flash successfully unlocked\n");
} else {
puts("Error
unlocking NAND flash, "
"write and erase will probably fail\n");
return
1;
}
return 0;
}
usage:
printf("Usage:\n%s\n",
cmdtp->usage);
return 1;
}
---------------------------do_nand函数结束----------------------------
bootm 0x30007fc0
--------------------以上分析了uboot读出内核,下面要开始分析如何启动内核-----------------------
主要是do_bootm函数,flash上存储的内核是uImage格式(头部+真正的内核)
-----------------------do_bootm函数开始----------------------------
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 =
CFG_BOOTM_LEN;
int i, verify;
char *name, *s;
int (*appl)(int, char
*[]);
image_header_t *hdr =
&header;
注释开始
这里就是uimage的头部,是一个结构体
typedef struct image_header {
uint32_t ih_magic; uint32_t ih_hcrc; uint32_t ih_time; uint32_t ih_size; uint32_t ih_load; 表示内核运行的时候你要把它放在哪里。
uint32_t ih_ep; 运行内核的时候入口地址,之前设置的是0x30007fc0,只要不破坏uboot内存使用分配即可,因为sp后面还有几十M的空间。正是因为uimage中有个header,header结构体中有一个loadaddress。我们把uimage放在某个地址,bootm加上这个地址,去读出头部中的in_load,如果发现内核不在加载地址中,则需要把内核移动到这个加载地址中去,最后跳到in_ep去执行。
uint32_t ih_dcrc; uint8_t ih_os; uint8_t ih_arch; uint8_t ih_type; uint8_t ih_comp; uint8_t ih_name[IH_NMLEN]; } image_header_t;
注释结束
s = getenv ("verify");
verify = (s &&
(*s == 'n')) ? 0 : 1;
if (argc < 2) {
addr = load_addr;
} else {
addr = simple_strtoul(argv[1],
NULL, 16);
}
SHOW_BOOT_PROGRESS (1);
printf ("## Booting image at lx ...\n",
addr);
#ifdef CONFIG_HAS_DATAFLASH
if (addr_dataflash(addr)){
read_dataflash(addr,
sizeof(image_header_t), (char *)&header);
} else
#endif
memmove (&header, (char *)addr,
sizeof(image_header_t));
if (ntohl(hdr->ih_magic) !=
IH_MAGIC) {
#ifdef __I386__ if (fake_header(hdr,
(void*)addr, -1) != NULL) {
addr -=
sizeof(image_header_t);
verify =
0;
} else
#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);
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
print_image_hdr ((image_header_t *)addr);
data = addr + sizeof(image_header_t);
len =
ntohl(hdr->ih_size);
if (verify) {
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__)
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)
#elif defined(__microblaze__)
if (hdr->ih_arch !=
IH_CPU_MICROBLAZE)
#elif defined(__nios2__)
if (hdr->ih_arch !=
IH_CPU_NIOS2)
#elif defined(__blackfin__)
if (hdr->ih_arch !=
IH_CPU_BLACKFIN)
#elif defined(__avr32__)
if (hdr->ih_arch !=
IH_CPU_AVR32)
#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";
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]);
data += 8;
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);
iflag = disable_interrupts();
#ifdef CONFIG_AMIGAONEG3SE
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) {如果ih_load==addr,则打印xip
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 否则就要移动真正的内核
memmove
((void *) ntohl(hdr->ih_load), (uchar *)data,
len);这里就是把真正内核开始的data移动到ih_load加载地址中去。韦东山开发板的内核真正地址为0x30008000,而内核头部的起始地址是0x30007fc0,两者相差64字节,正好是头部的长度,这样就不用做搬运真正的内核的工作了。可以加快启动速度。
#endif }
break;
case IH_COMP_GZIP:
printf
(" Uncompressing %s ... ",
name);
if (gunzip ((void
*)ntohl(hdr->ih_load), unc_len,
(uchar *)data, &len) != 0) {
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);
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
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();
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:
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: case IH_OS_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
#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
puts ("\n## Control returned to monitor -
resetting...\n");
do_reset (cmdtp, flag, argc, argv);
#endif
return 1;
}
-----------------------do_bootm函数结束----------------------------
do_bootm有两个作用:
作用1:读取内核头部将内核移动到合适地方,还有一些校验
作用2:启动内核,用的是do_bootm_linux函数。在跳到ih_ep入口之前还要uboot设置内核启动参数,然后才是跳到ih_ep启动内核。
------------------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");
#endif
theKernel = (void (*)(int, int,
uint))ntohl(hdr->ih_ep);
if (argc >= 3) {
SHOW_BOOT_PROGRESS (9);
addr = simple_strtoul
(argv[2], NULL, 16);
printf ("## Loading Ramdisk
Image at lx ...\n", addr);
#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)
memmove ((void *)
ntohl(hdr->ih_load), (uchar *)data, len);
data =
ntohl(hdr->ih_load);
#endif
} else if ((hdr->ih_type ==
IH_TYPE_MULTI) && (len_ptr[1]))
{
ulong tail = ntohl (len_ptr[0])
% 4;
int i;
SHOW_BOOT_PROGRESS (13);
data = (ulong)
(&len_ptr[2]);
for (i = 1; len_ptr[i];
++i)
data +=
4;
data += ntohl
(len_ptr[0]);
if (tail) {
data += 4 -
tail;
}
len = ntohl
(len_ptr[1]);
} else {
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 lx) ...\n",
(ulong) theKernel);
#if defined (CONFIG_SETUP_MEMORY_TAGS) || \ uboot设置参数在这里
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);
#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
printf ("\nStarting kernel ...\n\n");
#ifdef CONFIG_USB_DEVICE
{
extern void udc_disconnect
(void);
udc_disconnect ();
}
#endif
cleanup_before_linux ();
theKernel (0,
bd->bi_arch_number,
bd->bi_boot_params);启动内核在这里
}
------------------do_bootm_linux函数结束----------------------------
do_bootm_linux
作用1:设置内核启动参数,参数的格式是tag,对于韦东山的开发板地址是0x30000100,下面分析两个参数,其中setup_start_tag和setup_end_tag是必须的。
static void setup_start_tag (bd_t *bd)
{
params = (struct tag *)
bd->bi_boot_params;bi_boot_params在代码中搜索发现是
params->hdr.tag =
ATAG_CORE;
params->hdr.size = tag_size
(tag_core);
params->u.core.flags = 0;
params->u.core.pagesize = 0;
params->u.core.rootdev = 0;
params = tag_next (params);
}
作用2:跳到入口地址去是
theKernel = (void (*)(int, int,
uint))ntohl(hdr->ih_ep);
theKernel (0, bd->bi_arch_number,
bd->bi_boot_params);
这样就启动内核了!!!
具体bi_boot_params是多少搜索代码可以知道,其实韦东山是在自己的100ask24x0.c中自己定义的。
setup_start_tag之后我们得到:
static void setup_start_tag (bd_t *bd)
{
params = (struct tag *)
bd->bi_boot_params;
params->hdr.tag =
ATAG_CORE;
params->hdr.size = tag_size
(tag_core);
params->u.core.flags = 0;
params->u.core.pagesize = 0;
params->u.core.rootdev = 0;
params = tag_next (params);
}
0x30000100|size|tag|flag|page_size|root_dev|
其中header_size=sizeof(struct tag_header) + (sizeof(struct type)
>> 2)
也就是说执行完这个函数之后,要明白在内核启动参数区域都放了大小是多少的参数。
执行完setup_memory_tag函数之后:
#ifdef CONFIG_SETUP_MEMORY_TAGS
static void setup_memory_tags (bd_t *bd)
{
int i;
for (i = 0; i <
CONFIG_NR_DRAM_BANKS; i++) {
params->hdr.tag
= ATAG_MEM;
params->hdr.size
= tag_size (tag_mem32);
params->u.mem.start
= bd->bi_dram[i].start;
params->u.mem.size
= bd->bi_dram[i].size;
params = tag_next
(params);
}
}
#endif
这时存储内核启动参数的区域类似于setup_start_tag
开始是size|tag|size|start|
下面是start_commandline_tag
static void setup_commandline_tag (bd_t *bd, char
*commandline)
{
char *p;
if (!commandline)
return;
把命令前面的空格给干掉
for (p = commandline; *p == ' '; p++);
if (*p == '\0')
return;
params->hdr.tag =
ATAG_CMDLINE;
params->hdr.size =
(sizeof (struct tag_header) +
strlen (p) + 1 + 4) >> 2;
strcpy
(params->u.cmdline.cmdline, p);
params = tag_next (params);
}
commandline被传入一个参数*commandline,而这个参数是getev("bootargs"),用print命令在uboot命令行中查看bootargs,其中包括了内核的console的信息从哪里打印出来,是同ttyssa0也即串口0打出来。
size|tag|bootargs
-------------------最后一个是setup_end_tag--------------------
static void setup_end_tag (bd_t *bd)
{
params->hdr.tag = ATAG_NONE;
params->hdr.size = 0;
}
这两个参数全是0。