go设置后端启动_「正点原子Linux连载」第三十二章U-Boot启动流程详解(四)

1)实验平台:正点原子Linux开发板

2)摘自《正点原子I.MX6U嵌入式Linux驱动开发指南

关注官方微信号公众号,获取更多资料:正点原子

9390fdce94bb3029061e463ebb8cd592.png

32.3 bootz启动Linux内核过程

32.3.1 images全局变量

不管是bootz还是bootm命令,在启动Linux内核的时候都会用到一个重要的全局变量:images,images在文件cmd/bootm.c中有如下定义:

示例代码32.3.1.1 images全局变量

43bootm_headers_t images;/* pointers to os/initrd/fdt images */

images是bootm_headers_t类型的全局变量,bootm_headers_t是个boot头结构体,在文件include/image.h中的定义如下(删除了一些条件编译代码):

示例代码32.3.1.2 bootm_headers_t结构体

304typedefstruct bootm_headers {

305/*

306 * Legacy os image header, if it is a multi component image

307 * then boot_get_ramdisk() and get_fdt() will attempt to get

308 * data from second and third component accordingly.

309 */

310 image_header_t *legacy_hdr_os; /* image header pointer */

311 image_header_t legacy_hdr_os_copy;/* header copy */

312 ulong legacy_hdr_valid;

313

......

333

334 #ifndef USE_HOSTCC

335 image_info_t os; /* OS镜像信息 */

336 ulong ep; /* OS入口点 */

337

338 ulong rd_start, rd_end; /* ramdisk开始和结束位置 */

339

340char*ft_addr; /* 设备树地址 */

341 ulong ft_len; /* 设备树长度 */

342

343 ulong initrd_start; /* initrd开始位置 */

344 ulong initrd_end; /* initrd结束位置 */

345 ulong cmdline_start; /* cmdline开始位置 */

346 ulong cmdline_end; /* cmdline结束位置 */

347 bd_t *kbd;

348 #endif

349

350int verify;/* getenv("verify")[0] != 'n' */

351

352 #define BOOTM_STATE_START (0x00000001)

353 #define BOOTM_STATE_FINDOS (0x00000002)

354 #define BOOTM_STATE_FINDOTHER (0x00000004)

355 #define BOOTM_STATE_LOADOS (0x00000008)

356 #define BOOTM_STATE_RAMDISK (0x00000010)

357 #define BOOTM_STATE_FDT (0x00000020)

358 #define BOOTM_STATE_OS_CMDLINE (0x00000040)

359 #define BOOTM_STATE_OS_BD_T (0x00000080)

360 #define BOOTM_STATE_OS_PREP (0x00000100)

361 #define BOOTM_STATE_OS_FAKE_GO (0x00000200)/*'Almost' run the OS*/

362 #define BOOTM_STATE_OS_GO (0x00000400)

363int state;

364

365 #ifdef CONFIG_LMB

366struct lmb lmb;/* 内存管理相关,不深入研究 */

367 #endif

368} bootm_headers_t;

第335行的os成员变量是image_info_t类型的,为系统镜像信息。

第352~362行这11个宏定义表示BOOT的不同阶段。

接下来看一下结构体image_info_t,也就是系统镜像信息结构体,此结构体在文件include/image.h中的定义如下:

示例代码32.3.1.3 image_info_t结构体

292typedefstruct image_info {

293 ulong start, end; /* blob开始和结束位置*/

294 ulong image_start, image_len; /* 镜像起始地址(包括blob)和长度 */

295 ulong load; /* 系统镜像加载地址*/

296uint8_t comp, type, os; /* 镜像压缩、类型,OS类型 */

297uint8_t arch; /* CPU架构 */

298} image_info_t;

全局变量images会在bootz命令的执行中频繁使用到,相当于Linux内核启动的“灵魂”。

32.3.2 do_bootz函数

bootz命令的执行函数为do_bootz,在文件cmd/bootm.c中有如下定义:

示例代码32.3.2.1 do_bootz函数

622int do_bootz(cmd_tbl_t *cmdtp,int flag,int argc,char*const argv[])

623{

624int ret;

625

626/* Consume 'bootz' */

627 argc--; argv++;

628

629if(bootz_start(cmdtp, flag, argc, argv,&images))

630return1;

631

632/*

633 * We are doing the BOOTM_STATE_LOADOS state ourselves, so must

634 * disable interrupts ourselves

635 */

636 bootm_disable_interrupts();

637

638 images.os.os = IH_OS_LINUX;

639 ret = do_bootm_states(cmdtp, flag, argc, argv,

640 BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |

641 BOOTM_STATE_OS_GO,

642&images,1);

643

644return ret;

645}

第629行,调用bootz_start函数,bootz_start函数执行过程参考32.3.3小节。

第636行,调用函数bootm_disable_interrupts关闭中断。

第638行,设置images.os.os为IH_OS_LINUX,也就是设置系统镜像为Linux,表示我们要启动的是Linux系统!后面会用到images.os.os来挑选具体的启动函数。

第639行,调用函数do_bootm_states来执行不同的BOOT阶段,这里要执行的BOOT阶段有:BOOTM_STATE_OS_PREP 、BOOTM_STATE_OS_FAKE_GO和BOOTM_STATE_OS_GO。

32.3.3 bootz_start函数

bootz_srart函数也定义在文件cmd/bootm.c中,函数内容如下:

示例代码32.3.3.1 bootz_start函数

578staticint bootz_start(cmd_tbl_t *cmdtp,int flag,int argc,

579char*const argv[], bootm_headers_t *images)

580{

581int ret;

582 ulong zi_start, zi_end;

583

584 ret = do_bootm_states(cmdtp, flag, argc, argv,

585 BOOTM_STATE_START, images,1);

586

587/* Setup Linux kernel zImage entry point */

588if(!argc){

589 images->ep = load_addr;

590 debug("* kernel: default image load address = 0x%08lx",

591 load_addr);

592}else{

593 images->ep = simple_strtoul(argv[0],NULL,16);

594 debug("* kernel: cmdline image address = 0x%08lx",

595 images->ep);

596}

597

598 ret = bootz_setup(images->ep,&zi_start,&zi_end);

599if(ret !=0)

600return1;

601

602 lmb_reserve(&images->lmb, images->ep, zi_end - zi_start);

603

604/*

605 * Handle the BOOTM_STATE_FINDOTHER state ourselves as we do not

606 * have a header that provide this informaiton.

607 */

608if(bootm_find_images(flag, argc, argv))

609return1;

610

......

619return0;

620}

第584行,调用函数do_bootm_states,执行BOOTM_STATE_START阶段。

第593行,设置images的ep成员变量,也就是系统镜像的入口点,使用bootz命令启动系统的时候就会设置系统在DRAM中的存储位置,这个存储位置就是系统镜像的入口点,因此images->ep=0X80800000。

第598行,调用bootz_setup函数,此函数会判断当前的系统镜像文件是否为Linux的镜像文件,并且会打印出镜像相关信息,bootz_setup函数稍后会讲解。

第608行,调用函数bootm_find_images查找ramdisk和设备树(dtb)文件,但是我们没有用到ramdisk,因此此函数在这里仅仅用于查找设备树(dtb)文件,此函数稍后也会讲解。

先来看一下bootz_setup函数,此函数定义在文件arch/arm/lib/bootm.c中,函数内容如下:

示例代码32.3.3.2 bootz_setup函数

370 #define LINUX_ARM_ZIMAGE_MAGIC 0x016f2818

371

372int bootz_setup(ulong image, ulong *start, ulong *end)

373{

374struct zimage_header *zi;

375

376 zi =(struct zimage_header *)map_sysmem(image,0);

377if(zi->zi_magic != LINUX_ARM_ZIMAGE_MAGIC){

378 puts("Bad Linux ARM zImage magic!");

379return1;

380}

381

382*start = zi->zi_start;

383*end = zi->zi_end;

384

385 printf("Kernel image @ %#08lx [ %#08lx - %#08lx ]", image,

386 *start,*end);

387

388return0;

389}

第370行,宏LINUX_ARM_ZIMAGE_MAGIC就是ARM Linux系统魔术数。

第376行,从传递进来的参数image(也就是系统镜像首地址)中获取zimage头。zImage头结构体为zimage_header。

第377~380行,判断image是否为ARM的Linux系统镜像,如果不是的话就直接返回,并且打印出“Bad Linux ARM zImage magic!”,比如我们输入一个错误的启动命令:

bootz 80000000 – 900000000

因为我们并没有在0X80000000处存放Linux镜像文件(zImage),因此上面的命令肯定会执行出错的,结果如图32.3.3.1所示:

27b02e8f7c50fc1a090c7bce2e10f9bb.png

图32.3.3.1 启动出错

第382、383行初始化函数bootz_setup的参数start和end。

第385行,打印启动信息,如果Linux系统镜像正常的话就会输出图32.3.3.2所示的信息:

224974f63d724fecd00655a02ac658b3.png

图32.3.3.3 Linux镜像信息

接下来看一下函数bootm_find_images,此函数定义在文件common/bootm.c中,函数内容如下:

示例代码32.3.3.3 bootm_find_images函数

225int bootm_find_images(int flag,int argc,char*const argv[])

226{

227int ret;

228

229/* find ramdisk */

230 ret = boot_get_ramdisk(argc, argv,&images, IH_INITRD_ARCH,

231&images.rd_start,&images.rd_end);

232if(ret){

233 puts("Ramdisk image is corrupt or invalid");

234return1;

235}

236

237 #if defined(CONFIG_OF_LIBFDT)

238/* find flattened device tree */

239 ret = boot_get_fdt(flag, argc, argv, IH_ARCH_DEFAULT,&images,

240&images.ft_addr,&images.ft_len);

241if(ret){

242 puts("Could not find a valid device tree");

243return1;

244}

245 set_working_fdt_addr((ulong)images.ft_addr);

246 #endif

......

258return0;

259}

第230~235行是跟查找ramdisk,但是我们没有用到ramdisk,因此这部分代码不用管。

第237~244行是查找设备树(dtb)文件,找到以后就将设备树的起始地址和长度分别写到images的ft_addr和ft_len成员变量中。我们使用bootz启动Linux的时候已经指明了设备树在DRAM中的存储地址,因此images.ft_addr=0X83000000,长度根据具体的设备树文件而定,比如我现在使用的设备树文件长度为0X8C81,因此images.ft_len=0X8C81。

bootz_start函数就讲解到这里,bootz_start主要用于初始化images的相关成员变量。

32.3.4 do_bootm_states函数

do_bootz最后调用的就是函数do_bootm_states,而且在bootz_start中也调用了do_bootm_states函数,看来do_bootm_states函数还是个香饽饽。此函数定义在文件common/bootm.c中,函数代码如下:

示例代码32.3.4.1 do_bootm_states函数

591int do_bootm_states(cmd_tbl_t *cmdtp,int flag,int argc,char*const argv[],

592int states, bootm_headers_t *images,int boot_progress)

593{

594 boot_os_fn *boot_fn;

595 ulong iflag =0;

596int ret =0, need_boot_fn;

597

598 images->state |= states;

599

600/*

601 * Work through the states and see how far we get. We stop on

602 * any error.

603 */

604if(states & BOOTM_STATE_START)

605 ret = bootm_start(cmdtp, flag, argc, argv);

606

607if(!ret &&(states & BOOTM_STATE_FINDOS))

608 ret = bootm_find_os(cmdtp, flag, argc, argv);

609

610if(!ret &&(states & BOOTM_STATE_FINDOTHER)){

611 ret = bootm_find_other(cmdtp, flag, argc, argv);

612 argc =0;/* consume the args */

613}

614

615/* Load the OS */

616if(!ret &&(states & BOOTM_STATE_LOADOS)){

617 ulong load_end;

618

619 iflag = bootm_disable_interrupts();

620 ret = bootm_load_os(images,&load_end,0);

621if(ret ==0)

622 lmb_reserve(&images->lmb, images->os.load,

623(load_end - images->os.load));

624elseif(ret && ret != BOOTM_ERR_OVERLAP)

625goto err;

626elseif(ret == BOOTM_ERR_OVERLAP)

627 ret =0;

628 #if defined(CONFIG_SILENT_CONSOLE)&&!defined(CONFIG_SILENT_U_BOOT_ONLY)

629if(images->os.os == IH_OS_LINUX)

630 fixup_silent_linux();

631 #endif

632}

633

634/* Relocate the ramdisk */

635 #ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH

636if(!ret &&(states & BOOTM_STATE_RAMDISK)){

637 ulong rd_len = images->rd_end - images->rd_start;

638

639 ret = boot_ramdisk_high(&images->lmb, images->rd_start,

640 rd_len,&images->initrd_start,&images->initrd_end);

641if(!ret){

642 setenv_hex("initrd_start", images->initrd_start);

643 setenv_hex("initrd_end", images->initrd_end);

644}

645}

646 #endif

647 #if defined(CONFIG_OF_LIBFDT)&& defined(CONFIG_LMB)

648if(!ret &&(states & BOOTM_STATE_FDT)){

649 boot_fdt_add_mem_rsv_regions(&images->lmb, images->ft_addr);

650 ret = boot_relocate_fdt(&images->lmb,&images->ft_addr,

651&images->ft_len);

652}

653 #endif

654

655/* From now on, we need the OS boot function */

656if(ret)

657return ret;

658 boot_fn = bootm_os_get_boot_func(images->os.os);

659 need_boot_fn = states &(BOOTM_STATE_OS_CMDLINE |

660 BOOTM_STATE_OS_BD_T | BOOTM_STATE_OS_PREP |

661 BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO);

662if(boot_fn ==NULL&& need_boot_fn){

663if(iflag)

664 enable_interrupts();

665 printf("ERROR: booting os '%s' (%d) is not supported",

666 genimg_get_os_name(images->os.os), images->os.os);

667 bootstage_error(BOOTSTAGE_ID_CHECK_BOOT_OS);

668return1;

669}

670

671/* Call various other states that are not generally used */

672if(!ret &&(states & BOOTM_STATE_OS_CMDLINE))

673 ret = boot_fn(BOOTM_STATE_OS_CMDLINE, argc, argv, images);

674if(!ret &&(states & BOOTM_STATE_OS_BD_T))

675 ret = boot_fn(BOOTM_STATE_OS_BD_T, argc, argv, images);

676if(!ret &&(states & BOOTM_STATE_OS_PREP))

677 ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images);

678

679 #ifdef CONFIG_TRACE

680/* Pretend to run the OS, then run a user command */

681if(!ret &&(states & BOOTM_STATE_OS_FAKE_GO)){

682char*cmd_list = getenv("fakegocmd");

683

684 ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_FAKE_GO,

685 images, boot_fn);

686if(!ret && cmd_list)

687 ret = run_command_list(cmd_list,-1, flag);

688}

689 #endif

690

691/* Check for unsupported subcommand. */

692if(ret){

693 puts("subcommand not supported");

694return ret;

695}

696

697/* Now run the OS! We hope this doesn't return */

698if(!ret &&(states & BOOTM_STATE_OS_GO))

699 ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO,

700 images, boot_fn);

......

712return ret;

713}

函数do_bootm_states根据不同的BOOT状态执行不同的代码段,通过如下代码来判断BOOT状态:

states & BOOTM_STATE_XXX

在do_bootz函数中会用到BOOTM_STATE_OS_PREP 、BOOTM_STATE_OS_FAKE_GO和BOOTM_STATE_OS_GO这三个BOOT状态,bootz_start函数中会用到BOOTM_STATE_START这个BOOT状态。为了精简代码,方便分析,因此我们将示例代码32.3.4.1中的函数do_bootm_states进行精简,只留下下面这4个BOOT状态对应的处理代码:

BOOTM_STATE_OS_PREP

BOOTM_STATE_OS_FAKE_GO

BOOTM_STATE_OS_GO

BOOTM_STATE_START

精简以后的do_bootm_states函数如下所示:

示例代码32.3.4.2 精简后的do_bootm_states函数

591int do_bootm_states(cmd_tbl_t *cmdtp,int flag,int argc,char*const argv[],

592int states, bootm_headers_t *images,int boot_progress)

593{

594 boot_os_fn *boot_fn;

595 ulong iflag =0;

596int ret =0, need_boot_fn;

597

598 images->state |= states;

599

600/*

601 * Work through the states and see how far we get. We stop on

602 * any error.

603 */

604if(states & BOOTM_STATE_START)

605 ret = bootm_start(cmdtp, flag, argc, argv);

......

654

655/* From now on, we need the OS boot function */

656if(ret)

657return ret;

658 boot_fn = bootm_os_get_boot_func(images->os.os);

659 need_boot_fn = states &(BOOTM_STATE_OS_CMDLINE |

660 BOOTM_STATE_OS_BD_T | BOOTM_STATE_OS_PREP |

661 BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO);

662if(boot_fn ==NULL&& need_boot_fn){

663if(iflag)

664 enable_interrupts();

665 printf("ERROR: booting os '%s' (%d) is not supported",

666 genimg_get_os_name(images->os.os), images->os.os);

667 bootstage_error(BOOTSTAGE_ID_CHECK_BOOT_OS);

668return1;

669}

670

......

676if(!ret &&(states & BOOTM_STATE_OS_PREP))

677 ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images);

678

679 #ifdef CONFIG_TRACE

680/* Pretend to run the OS, then run a user command */

681if(!ret &&(states & BOOTM_STATE_OS_FAKE_GO)){

682char*cmd_list = getenv("fakegocmd");

683

684 ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_FAKE_GO,

685 images, boot_fn);

686if(!ret && cmd_list)

687 ret = run_command_list(cmd_list,-1, flag);

688}

689 #endif

690

691/* Check for unsupported subcommand. */

692if(ret){

693 puts("subcommand not supported");

694return ret;

695}

696

697/* Now run the OS! We hope this doesn't return */

698if(!ret &&(states & BOOTM_STATE_OS_GO))

699 ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO,

700 images, boot_fn);

......

712return ret;

713}

第604、605行,处理BOOTM_STATE_START阶段,bootz_start会执行这一段代码,这里调用函数bootm_start,此函数定义在文件common/bootm.c中,函数内容如下:

示例代码32.3.4.2 bootm_start函数

69staticint bootm_start(cmd_tbl_t *cmdtp,int flag,int argc,

70char*const argv[])

71{

72 memset((void*)&images,0,sizeof(images));/* 清空images */

73 images.verify = getenv_yesno("verify");/* 初始化verfify成员 */

74

75 boot_start_lmb(&images);

76

77 bootstage_mark_name(BOOTSTAGE_ID_BOOTM_START,"bootm_start");

78 images.state = BOOTM_STATE_START;/* 设置状态为BOOTM_STATE_START */

79

80 return0;

81}

接着回到示例代码32.3.4.2中,继续分析函数do_bootm_states。第658行非常重要!通过函数bootm_os_get_boot_func来查找系统启动函数,参数images->os.os就是系统类型,根据这个系统类型来选择对应的启动函数,在do_bootz中设置images.os.os= IH_OS_LINUX。函数返回值就是找到的系统启动函数,这里找到的Linux系统启动函数为do_bootm_linux,关于此函数查找系统启动函数的过程请参考32.3.5小节。因此boot_fn=do_bootm_linux,后面执行boot_fn函数的地方实际上是执行的do_bootm_linux函数。

第676行,处理BOOTM_STATE_OS_PREP状态,调用函数do_bootm_linux,do_bootm_linux也是调用boot_prep_linux来完成具体的处理过程。boot_prep_linux主要用于处理环境变量bootargs,bootargs保存着传递给Linuxkernel的参数。

第679~689行是处理BOOTM_STATE_OS_FAKE_GO状态的,但是要我们没用使能TRACE功能,因此宏CONFIG_TRACE也就没有定义,所以这段程序不会编译。

第699行,调用函数boot_selected_os启动Linux内核,此函数第4个参数为Linux系统镜像头,第5个参数就是Linux系统启动函数do_bootm_linux。boot_selected_os函数定义在文件common/bootm_os.c中,函数内容如下:

示例代码32.3.4.3 boot_selected_os函数

476int boot_selected_os(int argc,char*const argv[],int state,

477 bootm_headers_t *images, boot_os_fn *boot_fn)

478{

479 arch_preboot_os();

480 boot_fn(state, argc, argv, images);

......

490return BOOTM_ERR_RESET;

491}

第480行调用boot_fn函数,也就是do_bootm_linux函数来启动Linux内核。

32.3.5 bootm_os_get_boot_func函数

do_bootm_states会调用bootm_os_get_boot_func来查找对应系统的启动函数,此函数定义在文件common/bootm_os.c中,函数内容如下:

示例代码32.3.5.1 bootm_os_get_boot_func函数

493 boot_os_fn *bootm_os_get_boot_func(int os)

494{

495 #ifdef CONFIG_NEEDS_MANUAL_RELOC

496staticbool relocated;

497

498if(!relocated){

499int i;

500

501/* relocate boot function table */

502for(i =0; i < ARRAY_SIZE(boot_os); i++)

503if(boot_os[i]!=NULL)

504 boot_os[i]+= gd->reloc_off;

505

506 relocated = true;

507}

508 #endif

509return boot_os[os];

510}

第495~508行是条件编译,在本uboot中没有用到,因此这段代码无效,只有509行有效。在509行中boot_os是个数组,这个数组里面存放着不同的系统对应的启动函数。boot_os也定义在文件common/bootm_os.c中,如下所示:

示例代码32.3.5.2 boot_os数组

435static boot_os_fn *boot_os[]={

436[IH_OS_U_BOOT]= do_bootm_standalone,

437 #ifdef CONFIG_BOOTM_LINUX

438[IH_OS_LINUX]= do_bootm_linux,

439 #endif

......

465 #ifdef CONFIG_BOOTM_OPENRTOS

466[IH_OS_OPENRTOS]= do_bootm_openrtos,

467 #endif

468};

第438行就是Linux系统对应的启动函数:do_bootm_linux。

32.3.6 do_bootm_linux函数

经过前面的分析,我们知道了do_bootm_linux就是最终启动Linux内核的函数,此函数定义在文件arch/arm/lib/bootm.c,函数内容如下:

示例代码32.3.6.1 do_bootm_linux函数

339int do_bootm_linux(int flag,int argc,char*const argv[],

340 bootm_headers_t *images)

341{

342/* No need for those on ARM */

343if(flag & BOOTM_STATE_OS_BD_T || flag & BOOTM_STATE_OS_CMDLINE)

344return-1;

345

346if(flag & BOOTM_STATE_OS_PREP){

347 boot_prep_linux(images);

348return0;

349}

350

351if(flag &(BOOTM_STATE_OS_GO | BOOTM_STATE_OS_FAKE_GO)){

352 boot_jump_linux(images, flag);

353return0;

354}

355

356 boot_prep_linux(images);

357 boot_jump_linux(images, flag);

358return0;

359}

第351行,如果参数flag等于BOOTM_STATE_OS_GO或者BOOTM_STATE_OS_FAKE_GO的话就执行boot_jump_linux函数。boot_selected_os函数在调用do_bootm_linux的时候会将flag设置为BOOTM_STATE_OS_GO。

第352行,执行函数boot_jump_linux(又来了一个函数,绕啊绕啊!心累!),此函数定义在文件arch/arm/lib/bootm.c中,函数内容如下:

示例代码32.3.6.2 boot_jump_linux函数

272staticvoid boot_jump_linux(bootm_headers_t *images,int flag)

273{

274 #ifdef CONFIG_ARM64

......

292 #else

293unsignedlong machid = gd->bd->bi_arch_number;

294char*s;

295void(*kernel_entry)(int zero,int arch, uint params);

296unsignedlong r2;

297int fake =(flag & BOOTM_STATE_OS_FAKE_GO);

298

299 kernel_entry =(void(*)(int,int, uint))images->ep;

300

301 s = getenv("machid");

302if(s){

303if(strict_strtoul(s,16,&machid)<0){

304 debug("strict_strtoul failed!");

305return;

306}

307 printf("Using machid 0x%lx from environment", machid);

308}

309

310 debug("## Transferring control to Linux (at address %08lx)"

311"...",(ulong) kernel_entry);

312 bootstage_mark(BOOTSTAGE_ID_RUN_OS);

313 announce_and_cleanup(fake);

314

315if(IMAGE_ENABLE_OF_LIBFDT && images->ft_len)

316 r2 =(unsignedlong)images->ft_addr;

317else

318 r2 = gd->bd->bi_boot_params;

319

......

328 kernel_entry(0, machid, r2);

329}

330 #endif

331}

第274~292行是64位ARM芯片对应的代码,Cortex-A7是32位芯片,因此用不到。

第293行,变量machid保存机器ID,如果不使用设备树的话这个机器ID会被传递给Linux内核,Linux内核会在自己的机器ID列表里面查找是否存在与uboot传递进来的machid匹配的项目,如果存在就说Linux内核支持这个机器,那么Linux就会启动!如果使用设备树的话这个machid就无效了,设备树存有一个“兼容性”这个属性,Linux内核会比较“兼容性”属性的值(字符串)来查看是否支持这个机器。

第295行,函数kernel_entry,看名字“内核_进入”,说明此函数是进入Linux内核的,也就是最终的大boos!!此函数有三个参数:zero,arch,params,第一个参数zero同样为0;第二个参数为机器ID;第三个参数ATAGS或者设备树(DTB)首地址,ATAGS是传统的方法,用于传递一些命令行信息啥的,如果使用设备树的话就要传递设备树(DTB)。

第299行,获取kernel_entry函数,函数kernel_entry并不是uboot定义的,而是Linux内核定义的,Linux内核镜像文件的第一行代码就是函数kernel_entry,而images->ep保存着Linux内核镜像的起始地址,而起始地址保存的不正是Linux内核第一行代码!

第313行,调用函数announce_and_cleanup来打印一些信息并做一些清理工作,此函数定义在文件arch/arm/lib/bootm.c中,函数内容如下:

示例代码32.3.6.3 announce_and_cleanup函数

72staticvoid announce_and_cleanup(int fake)

73{

74 printf("Starting kernel ...%s", fake ?

75 "(fake run for tracing)":"");

76 bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF,"start_kernel");

......

87 cleanup_before_linux();

88}

第74行,在启动Linux之前输出“Starting kernel ...”信息,如图32.3.6.1所示:

741718d69822838f3a2293f1f53a0f32.png

图32.3.6.1 系统启动提示信息

第87行调用cleanup_before_linux函数做一些清理工作。

继续回到示例代码32.3.6.2的函数boot_jump_linux,第315~318行是设置寄存器r2的值?为什么要设置r2的值呢?Linux内核一开始是汇编代码,因此函数kernel_entry就是个汇编函数。向汇编函数传递参数要使用r0、r1和r2(参数数量不超过3个的时候),所以r2寄存器就是函数kernel_entry的第三个参数。

第316行,如果使用设备树的话,r2应该是设备树的起始地址,而设备树地址保存在images的ftd_addr成员变量中。

第317行,如果不使用设备树的话,r2应该是uboot传递给Linux的参数起始地址,也就是环境变量bootargs的值,

第328行,调用kernel_entry函数进入Linux内核,此行将一去不复返,uboot的使命也就完成了,它可以安息了!

总结一下bootz命令的执行过程,如图32.3.6.2所示:

6ac113a9df7b1b66a8d72388add1a9f7.png

图32.3.6.2 bootz命令执行过程

到这里uboot的启动流程我们就讲解完成了,加上uboot顶层Makefile的分析,洋洋洒洒100多页,还是不少的!这也仅仅是uboot启动流程分析,当缕清了uboot的启动流程以后,后面移植uboot就会轻松很多。其实在工作中我们基本不需要这么详细的去了解uboot,半导体厂商提供给我们的uboot一般是可以直接用的,只要能跑起来,可以使用就可以了。但是作为学习,我们是必须去详细的了解一下uboot的启动流程,否则如果在工作中遇到问题我们连解决的方法都没有,都不知道该从哪里看起。但是呢,如果第一次就想弄懂uboot的整个启动流程还是有点困难的,所以如果没有看懂的话,不要紧!不要气馁,大多数人第一次看uboot启动流程基本都有各种各样的问题。

题外话:

相信大家看完本章以后基本都有一个感觉:长、复杂、绕!没错,当我第一次学习uboot的时候看到uboot启动流程的时候也是这个感觉,当时我也一脸懵逼,怎么这么复杂,这么长呢?尤其前面的汇编代码部分,还要涉及到ARM处理器架构的内容,当时也怀疑过自己是不是搞这一块的料。不过好在自己坚持下来了,uboot的启动流程我至少分析过7,8遍,各种版本的,零几年很古老的;12年、14年比较新的等等很多个版本的uboot。就I.MX6ULL使用的这个2016.03版本uboot我至少详细的分析了2遍,直至写完本章,大概花了1个月的时间。这期间查阅了各种资料,看了不知道多少篇博客,在这里感谢那些无私奉献的网友们。

相信很多朋友看完本章可能会想:我什么时候也能这么厉害,能够这么详细的分析uboot启动流程。甚至可能会有挫败感,还是那句话:不要气馁!千里之行始于足下,所有你羡慕的人都曾经痛苦过,挫败过。脚踏实地,一步一个脚印,一点一滴的积累,最终你也会成为你所羡慕的人。在嵌入式Linux这条道路上,有众多的学习者陪着你,大家相互搀扶,终能踏出一条康庄大道,祝所有的同学终有所获!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值