u-boot移植篇——u-boot启动流程分析 下

本系列使用u-boot版本为u-boot-2018.01;

前面的篇幅分析完board_init_r函数,完成了几乎都是初始化的工作,回到_main继续往下执行,执行到relocate_code函数;

relocate_code

relocate_code定义在arch/arm/lib/relocate.S文件中,原型如下:

/*
 * void relocate_code(addr_moni)
 *
 * This function relocates the monitor code.
 *
 * NOTE:
 * To prevent the code below from containing references with an R_ARM_ABS32
 * relocation record type, we never refer to linker-defined symbols directly.
 * Instead, we declare literals which contain their relative location with
 * respect to relocate_code, and at run time, add relocate_code back to them.
 */

ENTRY(relocate_code)
	ldr	r1, =__image_copy_start	/* r1 <- SRC &__image_copy_start */
	subs	r4, r0, r1		/* r4 <- relocation offset */
	beq	relocate_done		/* skip relocation */
	ldr	r2, =__image_copy_end	/* r2 <- SRC &__image_copy_end */

copy_loop:
	ldmia	r1!, {r10-r11}		/* copy from source address [r1]    */
	stmia	r0!, {r10-r11}		/* copy to   target address [r0]    */
	cmp	r1, r2			/* until source end address [r2]    */
	blo	copy_loop

	/*
	 * fix .rel.dyn relocations
	 */
	ldr	r2, =__rel_dyn_start	/* r2 <- SRC &__rel_dyn_start */
	ldr	r3, =__rel_dyn_end	/* r3 <- SRC &__rel_dyn_end */
fixloop:
	ldmia	r2!, {r0-r1}		/* (r0,r1) <- (SRC location,fixup) */
	and	r1, r1, #0xff
	cmp	r1, #R_ARM_RELATIVE
	bne	fixnext

	/* relative fix: increase location by offset */
	add	r0, r0, r4
	ldr	r1, [r0]
	add	r1, r1, r4
	str	r1, [r0]
fixnext:
	cmp	r2, r3
	blo	fixloop

relocate_done:

#ifdef __XSCALE__
	/*
	 * On xscale, icache must be invalidated and write buffers drained,
	 * even with cache disabled - 4.2.7 of xscale core developer's manual
	 */
	mcr	p15, 0, r0, c7, c7, 0	/* invalidate icache */
	mcr	p15, 0, r0, c7, c10, 4	/* drain write buffer */
#endif

	/* ARMv4- don't know bx lr but the assembler fails to see that */

#ifdef __ARM_ARCH_4__
	mov	pc, lr
#else
	bx	lr
#endif

ENDPROC(relocate_code)

该函数用于拷贝代码用,uboot链接脚本分析中提到,链接脚本决定内存空间的存放位置,通过u-boot.map文件可以检索到对应的标志的起始地址,如上述源码中的第14行,__image_copy_start,该标志位uboot拷贝的首地址开始地址0x400000,现在加载到寄存器r1中去;

  • 第15行,这里的r0是上篇中经过_main调用board_init_f_init_reserve函数完成计算的重定位后的地址,也就是uboot后面要拷贝到的地址;现在r4=r0-r1计算出uboot的偏移量存储到r4中;
  • 第16行,做了一个判断,针对r0-r1的结果进行判断,如果r0-r1==0,那代表着源地址和目的地址是相同的,则直接跳转执行relocate_done函数,否则继续进行后面的拷贝;
  • 第17行,r2存储了__image_copy_end地址,这是拷贝前原代码结束的地址,这些值都是可以检索或着计算的,这里不直接进行计算是因为大家编译出来的uboot长度存在差异,避免钻牛角尖,这里只说过程;
  • 第19行,正式开始拷贝,到这里总结前面各寄存器中存放的地址和意义,如下表:
寄存器备注
r0relocation,重定位后uboot要拷贝到的地址
r1__image_copy_start,原uboot的起始地址
r2__image_copy_end,原uboot的结束地址
r4relocation offset,新旧拷贝地址的偏移量,目前只用于一次判断
  • 第20行,这里涉及一个汇编指令ldmia,他的作用与stmfd相反,一般成对出现在中断现场,stmfd用于保护现场和ldmia用于恢复现场,这里使用的意思是ldmia r1!, {r10-r11},就是从r1的地址地址上的内容,保存到r10和r11中,r10和r11都是32位寄存器,所以每次单次拷贝2个32位的数据,拷贝完成,r1会自动更新,递增到下一个数据地址;

  • 第21行,汇编指令stmia,该指令与ldmia类似,不过前者是将寄存器中的值加载到地址,后者是将地址中的数据记载到寄存器,这里stmia r0!, {r10-r11}的意思是,将r10和r11寄存器中的数据依次装载到r0中,即将第20行从原地址拷贝到的数据装载到新地址,装载完成,r0也会自动更新递增地址;
    插播一张飞机票,这里对ldm和stm指令有比较简明的介绍!

  • 第23~24行,先是比较r1和r2的值,判断是否相等来判断是否拷贝完成,如果不相等则跳转回copy_loop,形成一个循环,直到拷贝完成;

  • 后面的.rel.dyn段的拷贝,没有具体分析,这里就不误导大家了;

  • 完成uboot的拷贝,接下来回到_main跳转执行relocate_vectors函数,该函数用于重定位向量表,只有一步操作比较重要,就是将uboot重定位完之后的地址,装载到CP15的VBAR寄存器中设置向量表偏移,该寄存器自行去学习

board_init_r

完成了前面这么多工作,_main往下先是清除了BSS段,然后又来到一个新重要函数board_init_r,执行board_init_r函数前,该函数有两个参数,一个是gd,一个是重定位后的目的地址,这个后面会二次说明,board_init_r函数原型如下:
源文件见:common/board.c

void board_init_r(gd_t *new_gd, ulong dest_addr)
{
	/*
	 * Set up the new global data pointer. So far only x86 does this
	 * here.
	 * TODO(sjg@chromium.org): Consider doing this for all archs, or
	 * dropping the new_gd parameter.
	 */
#if CONFIG_IS_ENABLED(X86_64)
	arch_setup_gd(new_gd);
#endif

#ifdef CONFIG_NEEDS_MANUAL_RELOC
	int i;
#endif

#if !defined(CONFIG_X86) && !defined(CONFIG_ARM) && !defined(CONFIG_ARM64)
	gd = new_gd;
#endif
	gd->flags &= ~GD_FLG_LOG_READY;

#ifdef CONFIG_NEEDS_MANUAL_RELOC
	for (i = 0; i < ARRAY_SIZE(init_sequence_r); i++)
		init_sequence_r[i] += gd->reloc_off;
#endif

	if (initcall_run_list(init_sequence_r))
		hang();

	/* NOTREACHED - run_main_loop() does not return */
	hang();
}

该函数主要完成board_init_f未完成的初始化外设工作,该函数的构造与board_init_f函数很像,也是通过initcall_run_list来执行函数数组内的函数,init_sequence_r节选如下,删去了注释和部分没用到的条件编译以及无关紧要的函数,如下:

static init_fnc_t init_sequence_r[] = {
	initr_trace,
	initr_reloc,
#ifdef CONFIG_ARM
	initr_caches,
#endif
	initr_reloc_global_data,
    ...
	initr_malloc,
	...
	initr_bootstage,	/* Needs malloc() but has its own timer */
	...
#ifdef CONFIG_DM
	initr_dm,
#endif
#if defined(CONFIG_ARM) || defined(CONFIG_NDS32)
	board_init,	/* Setup chipselects */
#endif

#ifdef CONFIG_CLOCKS
	set_cpu_clk_info, /* Setup clock information */
#endif
#ifdef CONFIG_EFI_LOADER
	efi_memory_init,
#endif
	stdio_init_tables,
	initr_serial,
	initr_announce,
	INIT_FUNC_WATCHDOG_RESET
	INIT_FUNC_WATCHDOG_RESET
	...
#ifdef CONFIG_ARCH_EARLY_INIT_R
	arch_early_init_r,
#endif
	power_init_board,
	INIT_FUNC_WATCHDOG_RESET
	...
#ifdef CONFIG_CMD_NAND
	initr_nand,
#endif
	...
#ifdef CONFIG_MMC
	initr_mmc,
#endif
	initr_env,
	...
	INIT_FUNC_WATCHDOG_RESET
	initr_secondary_cpu,
	INIT_FUNC_WATCHDOG_RESET
	...
	stdio_add_devices,
	initr_jumptable,
	...
	console_init_r,		/* fully init console as a device */
#ifdef CONFIG_DISPLAY_BOARDINFO_LATE
	console_announce_r,
	show_board_info,
#endif
	...
	INIT_FUNC_WATCHDOG_RESET
	...
	interrupt_init,
#ifdef CONFIG_ARM
	initr_enable_interrupts,
#endif
	...
#ifdef CONFIG_CMD_NET
	initr_ethaddr,
#endif
#ifdef CONFIG_BOARD_LATE_INIT
	board_late_init,
#endif

#ifdef CONFIG_CMD_NET
	INIT_FUNC_WATCHDOG_RESET
	initr_net,
#endif
	...
	run_main_loop,
};
  • 第2行,之前的文章提到过,启用CONFIG_TRACE宏用于初始化调试相关的内容;
  • 第3行,用于更新gd的flags成员,该成员用于记录gd状态的,这里用于标志重定位完成;在这里插入图片描述
    在这里插入图片描述
  • 第5行,initr_caches函数用于初始化cache并完成使能,这些字面理解就好,这些在移植工作中也不会进行修改的;
  • 第7行,initr_reloc_global_data函数初始化重定位后gd的一些成员变量;
  • 第9行,initr_malloc初始化malloc相关;
  • 第11行,initr_bootstage初始化bootstage标志ID,一个简单的操作,但是一定要在initr_malloc之后;
  • 第12行,初始化控制台相关内容;
  • 第13~15行,initr_dm函数, 保存先前的重定位驱动程序模型并开始新的驱动模型;
  • 第21行,set_cpu_clk_info初始化时钟信息并完成设置,里面有针对gd对ddr或者arm时钟频率的配置,这个要注意一下;
  • 第26行,stdio_init_tables暂时没搞明白具体做了啥;
  • 第27行,initr_serial,老朋友了,初始化串口的;
  • 第43行,initr_mmc初始化MMC、EMMC;
  • 第45行,initr_env初始化环境变量;
  • 第54行,console_init_r初始化控制台,初始化完成以后此函数会调用stdio_print_current_devices函数来打印出当前的控制台设备;
  • 第62行,interrupt_init初始化中断,紧着initr_enable_interrupts使能中断;
  • 第68行,initr_ethaddr函数,初始化网络地址,也就是获取MAC地址。读取环境变量“ethaddr”的值;
  • 第76行,initr_net函数,初始化网络设备,函数调用顺序为: initr_net->eth_initialize;
  • 第79行,run_main_loop,进入主循环;在这里插入图片描述
    当程序执行到这里,我们如果把板子接到串口调试的话,可以看到开始uboot的倒计时了,倒计时的时长一般为3秒,这个时间是我们可以修改的,这个后续会讲解到,当倒计时结束没有按下回车键,那么就会启动内核,我们熟悉的start kernel就出现了,如果在倒计时内敲下回车,我们就会进入uboot的shell,就是uboot的命令模式!

run_main_loop

run_main_loop的定义依旧在common/board_r.c下面

static int run_main_loop(void)
{
#ifdef CONFIG_SANDBOX
	sandbox_main_loop_init();
#endif
	/* main_loop() can return to retry autoboot, if so just run it again */
	for (;;)
		main_loop();
	return 0;
}
  • 我们直接来到第7行,for(;;)这是一个死循环,进入main_loop函数:

main_loop

main_loop定义在common/main.c

void main_loop(void)
{
	const char *s;

	bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");

#ifdef CONFIG_VERSION_VARIABLE
	env_set("ver", version_string);  /* set version variable */
#endif /* CONFIG_VERSION_VARIABLE */

	cli_init();

	run_preboot_environment_command();

#if defined(CONFIG_UPDATE_TFTP)
	update_tftp(0UL, NULL, NULL);
#endif /* CONFIG_UPDATE_TFTP */

	s = bootdelay_process();
	if (cli_process_fdt(&s))
		cli_secure_boot_cmd(s);

	autoboot_command(s);

	cli_loop();
	panic("No CLI available");
}
  • 第7~9行,如果定义了宏CONFIG_VERSION_VARIABLE的话就会执行函数setenv,设置变量ver的值为 version_string,也就是设置版本号环境变量;
  • 第11行,cli_init函数,初始化命令相关,初始化hush shell相关的变量,这里我没有深入的去研究,原型如下,CONFIG_HUSH_PARSER宏是定义了的; 在这里插入图片描述+ 第13行,run_preboot_environment_command函数于环境变量相关,perboot是一些预启动命令,一般不使用,直接跳过即可;
  • 第19行,bootdelay_process该函数比较重要,该函数开始读取我们对uboot配置的一些环境变量,包括前面提到的倒计时bootdelay、bootcmd这是uboot shell下执行的命令来源,倒计时的实现也在这里,这里做个简单的分析

bootdelay_process

该函数原型在common/autoboot.c下

const char *bootdelay_process(void)
{
	char *s;
	int bootdelay;
#ifdef CONFIG_BOOTCOUNT_LIMIT
	unsigned long bootcount = 0;
	unsigned long bootlimit = 0;
#endif /* CONFIG_BOOTCOUNT_LIMIT */

#ifdef CONFIG_BOOTCOUNT_LIMIT
	bootcount = bootcount_load();
	bootcount++;
	bootcount_store(bootcount);
	env_set_ulong("bootcount", bootcount);
	bootlimit = env_get_ulong("bootlimit", 10, 0);
#endif /* CONFIG_BOOTCOUNT_LIMIT */

	s = env_get("bootdelay");
	bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;

#ifdef CONFIG_OF_CONTROL
	bootdelay = fdtdec_get_config_int(gd->fdt_blob, "bootdelay",
			bootdelay);
#endif

	debug("### main_loop entered: bootdelay=%d\n\n", bootdelay);

#if defined(CONFIG_MENU_SHOW)
	bootdelay = menu_show(bootdelay);
#endif
	bootretry_init_cmd_timeout();

#ifdef CONFIG_POST
	if (gd->flags & GD_FLG_POSTFAIL) {
		s = env_get("failbootcmd");
	} else
#endif /* CONFIG_POST */
#ifdef CONFIG_BOOTCOUNT_LIMIT
	if (bootlimit && (bootcount > bootlimit)) {
		printf("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",
		       (unsigned)bootlimit);
		s = env_get("altbootcmd");
	} else
#endif /* CONFIG_BOOTCOUNT_LIMIT */
		s = env_get("bootcmd");

	process_fdt_options(gd->fdt_blob);
	stored_bootdelay = bootdelay;

	return s;
}
  • 第5~16行直接跳过,一般不定义CONFIG_BOOTCOUNT_LIMIT宏,该宏是配置实现一种检测重复重启的机制,跳过就好;
  • 第18行,开始调用env_get函数在环境变量(gd->env_buf)中提取"bootdelay"字段信息,然后返回到char *s;
  • 第19行,判断是否成功提取,提取成功则调用simple_strtol函数进行字符串到整形的转化,提取失败则使用CONFIG_BOOTDELAY宏的定义,这个值是可以修改的,根据需求改打改小都行;
  • 第21~24行,当然如果我们定义了CONFIG_OF_CONTROL宏,启用了设备树的话,也可以通过设备树中查询"bootdelay"字段;
  • 第28~31行,CONFIG_MENU_SHOW宏是一个附加功能,在uboot命令行下可以展示也给类似于menu菜单一样的选择项,这个后面会开个专栏来讲;
  • 我们直接来带第45行,从环境变量中获取"bootcmd"这个字段,这个字段很重要,关系到uboot的指令等,获取出来的内容将可以通过pri在uboot shell打印出来;还记得上一篇中提到的env_init函数吗?该函数中涉及到一个字符数组default_environment,原型如下:
static char default_environment[] = {
#else
const uchar default_environment[] = {
#endif
#ifdef	CONFIG_ENV_CALLBACK_LIST_DEFAULT
	ENV_CALLBACK_VAR "=" CONFIG_ENV_CALLBACK_LIST_DEFAULT "\0"
#endif
#ifdef	CONFIG_ENV_FLAGS_LIST_DEFAULT
	ENV_FLAGS_VAR "=" CONFIG_ENV_FLAGS_LIST_DEFAULT "\0"
#endif
#ifdef	CONFIG_USE_BOOTARGS
	"bootargs="	CONFIG_BOOTARGS			"\0"
#endif
#ifdef	CONFIG_BOOTCOMMAND
	"bootcmd="	CONFIG_BOOTCOMMAND		"\0"
#endif
#ifdef	CONFIG_RAMBOOTCOMMAND
	"ramboot="	CONFIG_RAMBOOTCOMMAND		"\0"
#endif
#ifdef	CONFIG_NFSBOOTCOMMAND
	"nfsboot="	CONFIG_NFSBOOTCOMMAND		"\0"
#endif
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
	"bootdelay="	__stringify(CONFIG_BOOTDELAY)	"\0"
#endif
#if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0)
	"baudrate="	__stringify(CONFIG_BAUDRATE)	"\0"
#endif
#ifdef	CONFIG_LOADS_ECHO
	"loads_echo="	__stringify(CONFIG_LOADS_ECHO)	"\0"
#endif
#ifdef	CONFIG_ETHPRIME
	"ethprime="	CONFIG_ETHPRIME			"\0"
#endif
#ifdef	CONFIG_IPADDR
	"ipaddr="	__stringify(CONFIG_IPADDR)	"\0"
#endif
#ifdef	CONFIG_SERVERIP
	"serverip="	__stringify(CONFIG_SERVERIP)	"\0"
#endif
#ifdef	CONFIG_SYS_AUTOLOAD
	"autoload="	CONFIG_SYS_AUTOLOAD		"\0"
#endif
#ifdef	CONFIG_PREBOOT
	"preboot="	CONFIG_PREBOOT			"\0"
#endif
#ifdef	CONFIG_ROOTPATH
	"rootpath="	CONFIG_ROOTPATH			"\0"
#endif
#ifdef	CONFIG_GATEWAYIP
	"gatewayip="	__stringify(CONFIG_GATEWAYIP)	"\0"
#endif
#ifdef	CONFIG_NETMASK
	"netmask="	__stringify(CONFIG_NETMASK)	"\0"
#endif
#ifdef	CONFIG_HOSTNAME
	"hostname="	__stringify(CONFIG_HOSTNAME)	"\0"
#endif
#ifdef	CONFIG_BOOTFILE
	"bootfile="	CONFIG_BOOTFILE			"\0"
#endif
#ifdef	CONFIG_LOADADDR
	"loadaddr="	__stringify(CONFIG_LOADADDR)	"\0"
#endif
#ifdef	CONFIG_CLOCKS_IN_MHZ
	"clocks_in_mhz=1\0"
#endif
#if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0)
	"pcidelay="	__stringify(CONFIG_PCI_BOOTDELAY)"\0"
#endif
#ifdef	CONFIG_ENV_VARS_UBOOT_CONFIG
	"arch="		CONFIG_SYS_ARCH			"\0"
#ifdef CONFIG_SYS_CPU
	"cpu="		CONFIG_SYS_CPU			"\0"
#endif
#ifdef CONFIG_SYS_BOARD
	"board="	CONFIG_SYS_BOARD		"\0"
	"board_name="	CONFIG_SYS_BOARD		"\0"
#endif
#ifdef CONFIG_SYS_VENDOR
	"vendor="	CONFIG_SYS_VENDOR		"\0"
#endif
#ifdef CONFIG_SYS_SOC
	"soc="		CONFIG_SYS_SOC			"\0"
#endif
#endif
#ifdef	CONFIG_EXTRA_ENV_SETTINGS
	CONFIG_EXTRA_ENV_SETTINGS
#endif
	"\0"
#ifdef DEFAULT_ENV_INSTANCE_EMBEDDED
	}
#endif
};

其中bootcmd就在其中,第14~16行,可以看到"bootcmd=" CONFIG_BOOTCOMMAND;而CONFIG_BOOTCOMMAND宏可以通过uboot的menuconfig配置,也可以通过头文件的形式写入去更改;
我们可以在uboot启动的过程中,通过回车键打断,在讲bootcmd通过echo打印出来即可看到当前bootcmd的内容,如下:
在这里插入图片描述
通过uboot下make menuconfig去配置如下:
在这里插入图片描述
使能Enable a defayult value for bootcmd即可通过make menuconfig去配置和更改;
也可以通过头文件去修改,比如include/config_distro_bootcmd.h下:
在这里插入图片描述

process_fdt_options

回归bootdelay_process函数,在第45行,s = env_get("bootcmd");提取到对应的字段信息后,执行第47行,执行process_fdt_options函数,该函数用于补充环境变量的,原型如下:

static void process_fdt_options(const void *blob)
{
#if defined(CONFIG_OF_CONTROL) && defined(CONFIG_SYS_TEXT_BASE)
	ulong addr;

	/* Add an env variable to point to a kernel payload, if available */
	addr = fdtdec_get_config_int(gd->fdt_blob, "kernel-offset", 0);
	if (addr)
		env_set_addr("kernaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr));

	/* Add an env variable to point to a root disk, if available */
	addr = fdtdec_get_config_int(gd->fdt_blob, "rootdisk-offset", 0);
	if (addr)
		env_set_addr("rootaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr));
#endif /* CONFIG_OF_CONTROL && CONFIG_SYS_TEXT_BASE */
}
  • 如果启用CONFIG_OF_CONTROL宏,用设备树去进行uboot的部分环境变量配置的话,第7、12行,分别去设备树中获取"kernel-offset""rootdisk-offset"字段,如果设备树中没有的话,第9、14行会分别针对"kernaddr""rootaddr"环境变量进行设置;不过我们一般也不会在设备树中对这些变量进行定义,所以在uboot中,一般不开启CONFIG_OF_CONTROL宏;

继续回归bootdelay_process函数,第48行,stored_bootdelay = bootdelay;在环境变量中获取到bootdelay延迟信息后直接赋值给全局变量stored_bootdelay,然后将获取到的bootcmd返回[上层调用](# main_loop);

回到main_loop函数中,跳转到第23行,执行autoboot_command(s);

autoboot_command

common/autoboot.c

void autoboot_command(const char *s)
{
	debug("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");

	if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) {
#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
		int prev = disable_ctrlc(1);	/* disable Control C checking */
#endif

		run_command_list(s, -1, 0);

#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
		disable_ctrlc(prev);	/* restore Control C checking */
#endif
	}

#ifdef CONFIG_MENUKEY
	if (menukey == CONFIG_MENUKEY) {
		s = env_get("menucmd");
		if (s)
			run_command_list(s, -1, 0);
	}
#endif /* CONFIG_MENUKEY */
}

前面通过bootdelay_process函数获取到的延迟秒数存放到全局变量stored_bootdelay以及"bootcmd"字段返回的字符串,作为参数传入autoboot_command,在上述源码的第5行中进行判断,其中abortboot(stored_bootdelay)就是进行计时的代码,我们所看到的倒计时就是通过该函数进行倒计时的;
倒计时的执行源码如下:

abortboot(stored_bootdelay)
	--> __abortboot(bootdelay)
static int __abortboot(int bootdelay)
{
	int abort = 0;
	unsigned long ts;

#ifdef CONFIG_MENUPROMPT
	printf(CONFIG_MENUPROMPT);
#else
	printf("Hit any key to stop autoboot: %2d ", bootdelay);
#endif

	/*
	 * Check if key already pressed
	 */
	if (tstc()) {	/* we got a key press	*/
		(void) getc();  /* consume input	*/
		puts("\b\b\b 0");
		abort = 1;	/* don't auto boot	*/
	}

	while ((bootdelay > 0) && (!abort)) {
		--bootdelay;
		/* delay 1000 ms */
		ts = get_timer(0);
		do {
			if (tstc()) {	/* we got a key press	*/
				abort  = 1;	/* don't auto boot	*/
				bootdelay = 0;	/* no more delay	*/
# ifdef CONFIG_MENUKEY
				menukey = getc();
# else
				(void) getc();  /* consume input	*/
# endif
				break;
			}
			udelay(10000);
		} while (!abort && get_timer(ts) < 1000);

		printf("\b\b\b%2d ", bootdelay);
	}

	putc('\n');

	return abort;
}

我们看第11行,printf("Hit any key to stop autoboot: %2d ", bootdelay);,是不是我们uboot启动的时候打印出来的,问我们是否停止autoboot,进入uboot shell,就是这里来的;
如果我们一直按下回车键或者中途按下回车键,第34行就会获取然后退出循环,并返回abort=1;
如果我们没有按下回车键,abortboot(stored_bootdelay)返回值为0,等待stored_bootdelay递减结束,进入执行autoboot_command函数的第10行run_command_list(s, -1, 0);,该函数会执行参数s获取到的一系列命令,也就是环境变量bootcmd的命令,里面保存着默认的启动命令,引导内核启动;
当我们abortboot(stored_bootdelay)返回值为1时,经过autoboot_command中的第5行,判断就失败了,就直接回到main_loop继续往下执行,进入main_loop源码第25行cli_loop()

cli_loop

源码在common/cli.c定义

void cli_loop(void)
{
#ifdef CONFIG_HUSH_PARSER
	parse_file_outer();
	/* This point is never reached */
	for (;;);
#elif defined(CONFIG_CMDLINE)
	cli_simple_loop();
#else
	printf("## U-Boot command line is disabled. Please enable CONFIG_CMDLINE\n");
#endif /*CONFIG_HUSH_PARSER*/
}

该函数主要负责命令处理,进入到uboot shell下,
在这里插入图片描述
自此uboot启动完成,后续将会针对uboot的一些细节进行一一剖析。

以上是个人工作学习的回顾总结,有帮助的可以点个赞!欢迎指正,谢谢!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大大棋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值