do_bootm_linux函数源码分析(PPC)
static 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 sp;
ulong len, checksum;
ulong initrd_start, initrd_end;
ulong cmd_start, cmd_end;
ulong initrd_high;
ulong data;
int initrd_copy_to_ram = 1;
char *cmdline;
char *s;
bd_t *kbd;
void (*kernel)(bd_t *, ulong, ulong, ulong, ulong); // 内核启动函数指针
image_header_t *hdr = &header;
…
/*
* Booting a (Linux) kernel image
*
* Allocate space for command line and board info - the
* address should be as high as possible within the reach of
* the kernel (see CFG_BOOTMAPSZ settings), but in unused
* memory, which means far enough below the current stack
* pointer.
*/
asm( "mr %0,1": "=r"(sp) : ); // 获取当前堆栈地址
sp -= 2048; // 为了不破坏Uboot的堆栈地址, 向下移动2K
// CFG_BOOTMAPSZ是linux内核启动代码可以做重新映射的最大地址, 所有的可以
// 被linux处理的数据都必须放在这个地址空间以内! 故当上面得到的地址大于
// CFG_BOOTMAPSZ时必须使之等于CFG_BOOTMAPSZ.
if (sp > CFG_BOOTMAPSZ) // 保证sp指针位于CFG_BOOTMAPSZ内
sp = CFG_BOOTMAPSZ;
sp &= ~0xF; // 堆栈指针16字节对齐
debug ("=> set upper limit to 0x%08lX\n", sp); // 打印堆栈上限地址
// 设置命令行地址(CFG_BARGSIZE=256), 基本上是在地址的最顶端了
cmdline = (char *)((sp - CFG_BARGSIZE) & ~0xF); // 16字节对齐
kbd = (bd_t *)(((ulong)cmdline - sizeof(bd_t)) & ~0xF); // kbd设置在命令行的下面
if ((s = getenv("bootargs")) == NULL) // 读取保存的命令行参数
s = "";
strcpy (cmdline, s); // 拷贝命令行参数到cmdline处
cmd_start = (ulong)&cmdline[0]; // 取命令行的起始和结束地址
cmd_end = cmd_start + strlen(cmdline); // 命令行结束地址
*kbd = *(gd->bd); // 拷贝开发板相关的参数到kbd中
if ((s = getenv ("clocks_in_mhz")) != NULL) { // 设置系统频率(MHz)
kbd->bi_intfreq /= 1000000L;
kbd->bi_busfreq /= 1000000L;
}
// 设置内核入口点做为kernel函数的入口
kernel = (void (*)(bd_t *, ulong, ulong, ulong, ulong))hdr->ih_ep;
// 检查参数中是否存在Ramdisk
if (argc >= 3) { // bootm的命令行的第三个参数,Ram disk
…
} else if ((hdr->ih_type==IH_TYPE_MULTI) && (len_ptr[1])) {
…
} else {
SHOW_BOOT_PROGRESS (14); // 启动的第14阶段
len = data = 0; // 设置data为0(不使用Ramdisk)
}
if (!data) { // 不使用 Ram disk
debug ("No initrd\n");
}
if (data) { // 使用 Ram disk需要加载Ramdisk盘
if (!initrd_copy_to_ram) { /* zero-copy ramdisk support */
initrd_start = data;
initrd_end = initrd_start + len;
} else {
initrd_start = (ulong)kbd - len;
initrd_start &= ~(4096 - 1); /* align on page */
if (initrd_high) {
ulong nsp;
/*
* the inital ramdisk does not need to be within
* CFG_BOOTMAPSZ as it is not accessed until after
* the mm system is initialised.
*
* do the stack bottom calculation again and see if
* the initrd will fit just below the monitor stack
* bottom without overwriting the area allocated
* above for command line args and board info.
*/
asm( "mr %0,1": "=r"(nsp) : );
nsp -= 2048; /* just to be sure */
nsp &= ~0xF;
if (nsp > initrd_high) /* limit as specified */
nsp = initrd_high;
nsp -= len;
nsp &= ~(4096 - 1); /* align on page */
if (nsp >= sp)
initrd_start = nsp;
}
SHOW_BOOT_PROGRESS (12);
debug ("## initrd at 0x%08lX ... 0x%08lX (len=%ld=0x%lX)\n",
data, data + len - 1, len, len);
initrd_end = initrd_start + len;
printf (" Loading Ramdisk to %08lx, end %08lx ... ",
initrd_start, initrd_end);
#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
{
size_t l = len;
void *to = (void *)initrd_start;
void *from = (void *)data;
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 *)initrd_start, (void *)data, len);
#endif /* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */
puts ("OK\n");
}
} else {
initrd_start = 0;
initrd_end = 0;
}
debug ("## Transferring control to Linux (at address %08lx) ...\n",
(ulong)kernel);
SHOW_BOOT_PROGRESS (15); // 第15阶段就开始转动内核执行了
// 传递给内核的参数列表:
// r3: 开发板相关信息
// r4: Ramdisk起始地址, 不使用则直接置0
// r5: Ramdisk结束地址, 不使用则直接置0
// r6: 命令行参数起始地址
// r7: 命令行参数结束地址
(*kernel) (kbd, initrd_start, initrd_end, cmd_start, cmd_end); // 启动内核
}
(本文章发表于psbec的个人blog,未经本人许可,不得用于商业用途。任何个人、媒体、其他网站不得私自抄袭;网络媒体转载请注明出处,增加原文链接,否则属于侵权行为。如有任何问题,请留言或者发邮件给psbec,地址)
阅读(1530) | 评论(0) | 转发(0) |