uboot 网络引导 linux,uboot引导linux内核命令bootm

uboot引导linux内核命令bootm

2019年04月20日 ⁄ 评论关闭

uboot中bootm命令的具体分析

/*本文的部分内容参考了http://blog.csdn.net/xitijie/article/details/7004737这篇文章,在此表示感谢*/

bootm要做的事情:

1. 读取头部,把内核拷贝到合适的地方

2. 把参数给内核准备好,并告诉内核参数的首地址

3. 引导内核

启动内核:

do_bootm_linux:

1. 设置参数,跳到入口地址theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);

参数的传递:在某个地址按某种格式,存放好数据

do_bootm

int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])

{

ulongiflag;

ulongaddr;

ulongdata, len, checksum;

ulong  *len_ptr;

uintunc_len = CFG_BOOTM_LEN;

inti, verify;

char*name, *s;

int(*appl)(int, char *[]);

image_header_t *hdr = &header;

s = getenv ("verify");  //为是否对镜像头做校验做准备,读取uboot的环境变量verify,

如果环境变量verify等于’n’,则局部变量verify赋值成为0;如果环境变量verify为空(即没有

定义环境变量verify)或者环境变量verify不等于’n’,则局部变量verify赋值成为1。

verify = (s && (*s == 'n')) ? 0 : 1;

if (argc < 2) { //获取镜像存放的内存首地址,如果参数个数小于2(即只是输入了bootm),

//使用缺省加载地址CFG_LOAD_ADDR;否则使用第二个参数作为加载地址。

addr = load_addr;

} 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

memmove (&header, (char *)addr, sizeof(image_header_t));//从镜像内存首地址读取镜像头部,为下面的分析校验做准备

if (ntohl(hdr->ih_magic) != IH_MAGIC) { //判断文件头中的幻数是否为IH_MAGIC,所以如果不是u-boot镜像格式,会输出提示信息”Bad Magic Number”

#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__ */

{

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);

len  = ntohl(hdr->ih_size);

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__)          //校验cpu类型是否正确

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";

/* 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

switch (hdr->ih_comp) { //根据镜像的压缩类型把内核镜像解压到指定的地址,一般是mkimage时-a指定的

那个地址,-a指定的那个地址也就是存储在镜像头里面的hdr->ih_load变量中,

什么时候指定的呢,其实就是在执行mkimage指令的时候,-a输入的时候,赋给的数值

case IH_COMP_NONE:

if(ntohl(hdr->ih_load) == addr) { //如果image header中指示的加载地址和bootm命令中参数2指定的地址相同,则表示不需要copy,可以就地执行。

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) */如果image header中指示的加载地址和bootm命令中参数2指定的地址不相同,则表示要从image header中指示的加载地址处把image data copy到bootm命令中参数2指定的地址处,然后再执行。

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, &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);

/*

* 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);

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  (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

puts ("\n## Control returned to monitor - resetting...\n");

do_reset (cmdtp, flag, argc, argv);

#endif

return 1;

}

do_bootm_linux函数

位置:在lib_arm/bootm.c中

59   int do_bootm_linux(intflag, int argc, char *argv[], bootm_headers_t *images)

60   {

61     bd_t      *bd = gd->bd;

62     char      *s;

63     int   machid =bd->bi_arch_number;

64     void      (*theKernel)(int zero, int arch, uint params);

65

66   #ifdefCONFIG_CMDLINE_TAG

67      char*commandline = getenv("bootargs");

68   #endif

//可以看到theKernel被赋值为hdr->ih_ep,这个hdr是指使用tools/mkimage工具程

//序制作uImage时加在linux.bin.gz前面的一个头部,而ih_ep结构体成员保存的就是使用mkimage时指定

//的-e参数的值,即内核的入口点(Entry Point)。知道了hdr->ih_ep的意义之后,给theKernel赋这个

//值也就是理所当然的了。

73      theKernel =(void (*)(int, int, uint))images->ep;    /* 获取内核加载地址 */

… …

86   #if defined(CONFIG_SETUP_MEMORY_TAGS) || \

87      defined (CONFIG_CMDLINE_TAG) || \

88      defined (CONFIG_INITRD_TAG) || \

89      defined (CONFIG_SERIAL_TAG) || \

90      defined (CONFIG_REVISION_TAG) || \

91      defined (CONFIG_LCD) || \

92      defined (CONFIG_VFD)

93     setup_start_tag(bd);

… …

100  #ifdef CONFIG_SETUP_MEMORY_TAGS

101    setup_memory_tags(bd);

102  #endif

103  #ifdef CONFIG_CMDLINE_TAG

104    setup_commandline_tag (bd,commandline);

105  #endif

… …

113    setup_end_tag(bd);

114  #endif

115

116

117     printf("\nStarting kernel ...\n\n");

… …

126    cleanup_before_linux();

127

128     theKernel(0, machid,bd->bi_boot_params);

129

130

131     return1;

132  }

其中的setup_start_tag

setup_memory_tags

setup_end_tag函数

位置: lib_arm/bootm.c

(1)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);

}

标记列表必须以ATAG_CORE开始,setup_start_tag函数在内核的参数的开始地址设置了一个ATAG_CORE标记。

(2)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);

}

}

setup_memory_tags函数设置了一个ATAG_MEM标记,该标记包含内存起始地址,内存大小这两个参数。

(3)setup_end_tag函数

static void setup_end_tag (bd_t *bd)

{

params->hdr.tag = ATAG_NONE;

params->hdr.size = 0;

}

标记列表必须以标记ATAG_NONE结束,setup_end_tag函数设置了一个ATAG_NONE标记,表示标记列表的结束。

U-Boot设置好标记列表后就要调用内核了。但调用内核前,CPU必须满足下面的条件:

(1)   CPU寄存器的设置

Ø  r0=0

Ø  r1=机器码

Ø  r2=内核参数标记列表在RAM中的起始地址

(2)   CPU工作模式

Ø  禁止IRQ与FIQ中断

Ø  CPU为SVC模式

(3)   使数据Cache与指令Cache失效

do_bootm_linux中调用的cleanup_before_linux函数完成了禁止中断和使Cache失效的功能。cleanup_before_linux函数在cpu/arm920t/cpu.中定义:

int cleanup_before_linux (void)

{

disable_interrupts();

icache_disable();

dcache_disable();

cache_flush();

return 0;

}

由于U-Boot启动以来就一直工作在SVC模式,因此CPU的工作模式就无需设置了。

do_bootm_linux中:

64     void      (*theKernel)(int zero, int arch, uint params);

… …

73      theKernel =(void (*)(int, int, uint))images->ep;

… …

128     theKernel(0, machid, bd->bi_boot_params);

第73行代码将内核的入口地址“images->ep”强制类型转换为函数指针。根据ATPCS规则,函数的参数个数不超过4个时,使用r0~r3这4个寄存器来传递参数。因此第128行的函数调用则会将0放入r0,机器码machid放入r1,内核参数地址bd->bi_boot_params放入r2,从而完成了寄存器的设置,最后转到内核的入口地址。

到这里,U-Boot的工作就结束了,系统跳转到Linux内核代码执行。

mkimage

bootm命令是用来引导经过u-boot的工具mkimage打包后的kernel image的,什么叫做经

过u-boot的工具mkimage打包后的kernel image,这个就要看mkimage的用法:

mkimage的用法

uboot源代码的tools/目录下有mkimage工具,这个工具可以用来制作不压缩或者压缩的多种可启动映象文件。

mkimage在制作映象文件的时候,是在原来的可执行映象文件的前面加上一个0x40字节的头,记录参数所指定的信息,这样uboot才能识别这个映象是针对哪个CPU体系结构的,哪个OS的,哪种类型,加载内存中的哪个位置, 入口点在内存的那个位置以及映象名是什么

引用:

root@Glym:/tftpboot# ./mkimage

Usage: ./mkimage -l image

-l ==> list image header information

./mkimage -A arch -O os -T type -C comp -a addr -e ep -n name -d data_file[:data_file...] image

-A ==> set architecture to 'arch'

-O ==> set operating system to 'os'

-T ==> set image type to 'type'

-C ==> set compression type 'comp'

-a ==> set load address to 'addr' (hex)

-e ==> set entry point to 'ep' (hex)

-n ==> set image name to 'name'

-d ==> use image data from 'datafile'

-x ==> set XIP (execute in place)

参数说明:

-A 指定CPU的体系结构:

取值 表示的体系结构

alpha Alpha

arm A RM

x86 Intel x86

ia64 IA64

mips MIPS

mips64 MIPS 64 Bit

ppc PowerPC

s390 IBM S390

sh SuperH

sparc SPARC

sparc64 SPARC 64 Bit

m68k MC68000

-O 指定操作系统类型,可以取以下值:

openbsd、netbsd、freebsd、4_4bsd、linux、svr4、esix、solaris、irix、sco、dell、ncr、lynxos、vxworks、psos、qnx、u-boot、rtems、artos

-T 指定映象类型,可以取以下值:

standalone、kernel、ramdisk、multi、firmware、script、filesystem

-C 指定映象压缩方式,可以取以下值:

none 不压缩

gzip 用gzip的压缩方式

bzip2 用bzip2的压缩方式

-a 指定映象在内存中的加载地址,映象下载到内存中时,要按照用mkimage制作映象时,这个参数所指定的地址值来下载

-e 指定映象运行的入口点地址,这个地址就是-a参数指定的值加上0x40(因为前面有个mkimage添加的0x40个字节的头)

-n 指定映象名

-d 指定制作映象的源文件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值