U-boot引导流程分析二

Stage II过程分析

在Stage II中使用到了一些比较重要的数据结构,这里先对这些数据结构来进行下分析:

typedef	struct	global_data {
	bd_t		*bd;
	unsigned long	flags;
	unsigned long	baudrate;
	unsigned long	have_console;	/* serial_init() was called */
	unsigned long	reloc_off;	/* Relocation Offset */
	unsigned long	env_addr;	/* Address  of Environment struct */
	unsigned long	env_valid;	/* Checksum of Environment valid? */
	unsigned long	fb_base;	/* base address of frame buffer */
#ifdef CONFIG_VFD
	unsigned char	vfd_type;	/* display type */
#endif
#if 0
	unsigned long	cpu_clk;	/* CPU clock in Hz!		*/
	unsigned long	bus_clk;
	unsigned long	ram_size;	/* RAM size */
	unsigned long	reset_status;	/* reset status register at boot */
#endif
	void		**jt;		/* jump table */
} gd_t;

gd_t数据结构在include/asm-arm/global_data.h中定义,它用来存储全局数据区的数据。

#define DECLARE_GLOBAL_DATA_PTR     register volatile gd_t *gd asm ("r8")

U-Boot中使用了一个存储在寄存器中的指针gd来记录全局数据区的地址。DECLARE_GLOBAL_DATA_PTR定义了一个gd_t全局数据结构的指针,这个指针存放在寄存器r8中。这个声明也避免编译器把r8分配各其他变量。任何想要访问全局数据区的代码,只要开头加入"DECLARE_GLOBAL_DATA_PTR"这一行代码,然后就可以使用gd指针来访问全局数据区了。

根据U-Boot内存使用图可以计算gd的值: gd = TEXT_BASE - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t)。

typedef struct bd_info {
    int			bi_baudrate;	/* serial console baudrate */
    unsigned long	bi_ip_addr;	/* IP Address */
    unsigned char	bi_enetaddr[6]; /* Ethernet adress */
    struct environment_s	       *bi_env;
    ulong	        bi_arch_number;	/* unique id for this board */
    ulong	        bi_boot_params;	/* where this board expects params */
    struct				/* RAM configuration */
    {
	ulong start;
	ulong size;
    } 			bi_dram[CONFIG_NR_DRAM_BANKS];
#ifdef CONFIG_HAS_ETH1
    /* second onboard ethernet port */
    unsigned char   bi_enet1addr[6];
#endif
} bd_t;

db_t数据结构在include/asm-arm/u-boot.h中定义。U-Boot启动内核时要给内核传递参数,这时就要使用gd_t,db_t结构体中的信息来设置标记列表。

init_fnc_t *init_sequence[] = {
	cpu_init,		/* basic cpu dependent setup 基本的处理器相关配置 --cpu/arm920t/cpu.c */
	board_init,		/* basic board dependent setup 基本的板级相关配置 --board/smdk2410/smdk2410.c */
	interrupt_init,	/* set up exceptions 初始化中断处理 --cpu/arm920t/s3c24x0/interrupts.c */
	env_init,		/* initialize environment 初始化环境变量 --common/env_flash.c */

	init_baudrate,		/* initialze baudrate settings 初始化波特率设置 --lib_arm/board.c */
	serial_init,		/* serial communications setup 串口通讯设置 --cpu/arm920t/serial.c */
	console_init_f,		/* stage 1 init of console 控制台初始化阶段1 -- common/console.c */
	display_banner,		/* say that we are here 打印U-Boot版本、编译的时间 --lib_arm/board.c */
#if defined(CONFIG_DISPLAY_CPUINFO)
	print_cpuinfo,		/* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
	checkboard,			/* display board info */
#endif
	dram_init,			/* configure available RAM banks 配置可用的RAM --board/smdk2410/smdk2410.c */
	/*
	 * 在这里开始进行配置SDRAM信息,用到结构体	bd->bi_dram[BANK_NR].start	bd->bi_dram[BANK_NR].size                                                                                                       bd->bi_dram[BANK_NR].size
	 */
	display_dram_config,	/* 显示SDRAM的配置信息 --lib_arm/board.c */

	NULL,
};

U-Boot使用一个数组init_sequence来存储对于大多数开发板都要执行的函数指针。

int cpu_init (void)
{
	/*
	 * setup up stacks if necessary
	 */
#ifdef CONFIG_USE_IRQ
	IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4;
	FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ;
#endif
	return 0;
}

其中的cpu_init函数在cpu/arm920t/cpu.c中定义。

int board_init (void)
{
	..............
	/* arch number of SMDK2410-Board */
	gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;	/* SMDK2410开发板的机器码(板子ID) */

	/* adress of boot parameters */
	gd->bd->bi_boot_params = 0x30000100;		/* 内核启动参数地址(绝对地址) */
	.............
}

board_init设置了U-Boot机器码和内核启动参数地址。

/* 初始化内存RAM的信息,其实就是给gd->bd中内存信息表赋值而已 */
int dram_init (void)
{
	/* 设置板级数据中的SDRAM开始地址和大小 */
	gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
	gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;

	return 0;
}

分析完上述数据结构,下面接着来分析Stage II入口函数start_armboot(实现在lib-arm/board.c中):

void start_armboot (void)
{
	init_fnc_t **init_fnc_ptr;
	char *s;
#ifndef CFG_NO_FLASH
	ulong size;
#endif

#if defined(CONFIG_VFD) || defined(CONFIG_LCD)
	unsigned long addr;
#endif

#if defined(CONFIG_BOOT_MOVINAND)
	uint *magic = (uint *) (PHYS_SDRAM_1);
#endif

	/*
	 * Pointer is writable since we allocated a register for it
	 *
	 * 在重定位之后,即uboot的代码从flash拷到sdram 此时连接脚本里的_start 等于TEXT_BASE
	 */
#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */
	ulong gd_base;

	gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t);
#ifdef CONFIG_USE_IRQ
	gd_base -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);
#endif
	gd = (gd_t*)gd_base;
#else
	/* 给全局变量gd分配空间大小且指定gd的位置 这里gd是一个结构体,在uboot内存分布中是CFG_GBL_DATA_SIZE一共128字节 */
	gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));	/* 计算全局数据结构的地址gd */
	/*
	 * 对全局数据区进行地址分配,_armboot_start为0x3f000000,CFG_MALLOC_LEN是堆大小+环境数据区大小,
	 * include/configs/smdk2410.h中CFG_MALLOC_LEN大小定义为192KB。
	 */
#endif

	/* compiler optimization barrier needed for GCC >= 3.4 */
	__asm__ __volatile__("": : :"memory");

	memset ((void*)gd, 0, sizeof (gd_t));			/* gd指针所指向的空间清零  */
	gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));		/* 给gd中的bd指针分配空间大小 */
	memset (gd->bd, 0, sizeof (bd_t));				/* gd->bd 所指向的空间清零 */

	monitor_flash_len = _bss_start - _armboot_start;	/* uboot镜像文件的大小 */

	/*
	 * 顺序执行init_sequence数组中的初始化函数
	 *
	 * 这里是调用了一系列的c函数指针,进行初始化。
	 * 比如cpu_init初始化完成各个gpio管脚初始化,board_init完成arch_number设置和boot_params约定存放地址,还有串口初始化等。
	 */
	for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
		if ((*init_fnc_ptr)() != 0) {
			hang ();
		}
	}

/*
 * 识别出来是哪一种Flash NOR还是NAND 如果定义了CFG_NO_FLASH这个宏,说明是NAND 否则是NOR
 *
 * NOR型Flash的初始化
 */
#ifndef CFG_NO_FLASH
	/* configure available FLASH banks 配置可用的flash空间,并打印出相关信息 */
	size = flash_init ();		/* NOR型Flash的初始化 */
	display_flash_config (size);
#endif /* CFG_NO_FLASH */

/*
 * 初始化VFD存储区(LCD显示相关)
 *
 * 定义显示类型 分vfd和lcd两种。vfd一般不用,我们用lcd的	在这里定义了帧缓冲,也就显存的的地址和大小
 */
#ifdef CONFIG_VFD
#	ifndef PAGE_SIZE
#	  define PAGE_SIZE 4096		/* 定义页大小为4K */
#	endif
	/*
	 * reserve memory for VFD display (always full pages)
	 */
	/* bss_end is defined in the board-specific linker script 把视频帧缓冲区设置在bss_end后面 */
	addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
	size = vfd_setmem (addr);
	gd->fb_base = addr;
#endif /* CONFIG_VFD */

/* 初始化LCD显存	在内存中配置一块帧缓冲区 */
#ifdef CONFIG_LCD
#	ifndef PAGE_SIZE
#	  define PAGE_SIZE 4096
#	endif
	/*
	 * reserve memory for LCD display (always full pages)
	 */
	/* bss_end is defined in the board-specific linker script */
#ifdef LCD_FRAMEBUFFER_ADDR
	addr =  (void*)LCD_FRAMEBUFFER_ADDR;
#else
	addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);		/* 按页对其方式保留显存 */
#endif
	size = lcd_setmem (addr);		/* 分配帧缓冲区的大小 */
	gd->fb_base = addr;				/* 帧缓冲区的物理起始地址 */
#endif /* CONFIG_LCD */

	/* armboot_start is defined in the board-specific linker script */
#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */
	mem_malloc_init (CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE);
#else
	mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);		/* 分配堆空间大小 */
	/*
	 * 初始化堆空间(设置heap区,供malloc使用)
	 * malloc可用内存由mem_malloc_start,mem_malloc_end指定。而当前分配的位置则是mem_malloc_brk。
	 * mem_malloc_init负责初始化这三个变量。malloc则通过sbrk函数来使用和管理这片内存。 --lib_arm/board.c
	 */
#endif

#if defined(CONFIG_SMDK6400) || defined(CONFIG_SMDK6410) || defined(CONFIG_SMDK6430) || defined(CONFIG_SMDK2450) || defined(CONFIG_SMDK2416)

/*
 * 初始化nand flash,这是在nand flash启动的s3c2410移植u-boot的关键,根据flash时序编写函数即可
 * 在include/configs/smdk2410.h中的command definition中增加CONFIG_COMMANDS和CFG_CMD_NAND命令
 * nand型flash的初始化
 */
#if defined(CONFIG_NAND)

#ifdef FORLINX_DEBUG
           printf("NandFlash Information:\n");
#else
           puts ("NAND:    ");
#endif

	nand_init();		/* go init the NAND 初始化Nand Flash控制器,获取NAND的基地址和大小信息 */
#endif

#if defined(CONFIG_ONENAND)		/* 三星的一种特别的flash  onenand,类似于nand */
	puts ("OneNAND: ");
	onenand_init();		/* go init the One-NAND 初始化OneNand */
#endif

#if defined(CONFIG_BOOT_MOVINAND)
	puts ("SD/MMC:  ");

	if ((0x24564236 == magic[0]) && (0x20764316 == magic[1])) {
		printf("Boot up for burning\n");
	} else {
		movi_set_capacity();
		movi_set_ofs(MOVI_TOTAL_BLKCNT);
		movi_init();
	}
#endif

#else

/*
 * NAND Flash初始化
 */
#if (CONFIG_COMMANDS & CFG_CMD_NAND)
	puts ("NAND:    ");
	nand_init();		/* go init the NAND */
#endif

#endif

/* 初始化DataFlash */
#ifdef CONFIG_HAS_DATAFLASH
	AT91F_DataflashInit();
	dataflash_print_info();
#endif

	/* initialize environment 配置环境变量,重新定位 --common/env_common.c */
	env_relocate ();

/* FrameBuffer初始化 */
#ifdef CONFIG_VFD
	/* must do this after the framebuffer is allocated */
	drv_vfd_init();		/* Video的初始化 */
#endif /* CONFIG_VFD */

	/*
	 * IP,MAC地址的初始化。主要是从环境中读,然后赋值给gd->bd对应域就OK了。
	 *
	 * IP Address 从环境变量中获取IP地址(开发板IP) 以太网接口MAC地址
	 * 主要是初始化 gd->bd->bi_ip_addr和gd->bd->bi_enetaddr[]
	 */
	gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");

	/* MAC Address 获得环境变量中的以太网接口MAC地址,设置到gd->bd-bi_enetaddr[reg]中 */
	{
		int i;
		ulong reg;
		char *s, *e;
		char tmp[64];

		i = getenv_r ("ethaddr", tmp, sizeof (tmp));
		s = (i > 0) ? tmp : NULL;

		for (reg = 0; reg < 6; ++reg) {
			gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
			if (s)
				s = (*e) ? e + 1 : e;
		}

#ifdef CONFIG_HAS_ETH1		/* 如果要是有两块网卡 */
		i = getenv_r ("eth1addr", tmp, sizeof (tmp));
		s = (i > 0) ? tmp : NULL;

		for (reg = 0; reg < 6; ++reg) {
			gd->bd->bi_enet1addr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
			if (s)
				s = (*e) ? e + 1 : e;
		}
#endif
	}

	devices_init ();	/* get the devices list going. 注册设备链表,其实也就只注册了一个串口设备 --common/devices.c */

#ifdef CONFIG_CMC_PU2
	load_sernum_ethaddr ();
#endif /* CONFIG_CMC_PU2 */

	jumptable_init ();	/* 跳转表初始化 --common/exports.c */

	console_init_r ();
	/*
	 * fully init console as a device
	 *
	 * 完整地初始化控制台设备 --common/console.c
	 * 到这里才可以从控制台看到数据打印出来
	 */

	 /*
	  * 从串口寄存器的设置到最终在终端上打印信息,是有以下函数组成的。
	  *		在init_sequense里的三个函数和
	  *		init_baudrate,   设置 gd->bd->bi_baudrate
	  *		serial_init,     直接调用serial_setbrg函数初始化UART寄存器:8个数据位,一个开始位,一个停止位,无校验位。。。
	  *		console_init_f,  控制台前期初始化  设置gd->have_console=1
	  *		devices_init,    调用drv_system_init 注册串口设备
	  *		console_init_r   控制台后期初始化,将串口设备指向控制台标准输入设备,标准输出设备,标准错误设备
	  *
	  *		In:   serial
      *		Out: serial
      *		Err: serial
	  *		打印这三行信息,表明串口作为标准输入设备,标准输出设备,标准错误输出设备,这样就能打印信息了
	  *		默认情况下,键盘和鼠标默认为标准输入设备,显示器默认为标准输出设备和标准错误输出设备,printf为标准格式输出
	  *		scanf为标准格式输入。标准输入,标准输出设备的重定向即将串口设备作为标准输入设备,将串口做为标准输出设备和
	  *		标准错误输出设备。
	  */

#if defined(CONFIG_MISC_INIT_R)
	/* miscellaneous platform dependent initialisations */
	misc_init_r ();		/* 混杂设备的初始化 */
#endif

	/* enable exceptions 使能中断处理 --cpu/arm920t/interrupts.c */
	enable_interrupts ();

	/* Perform network card initialisation if necessary */
#ifdef CONFIG_DRIVER_CS8900
	//cs8900_get_enetaddr (gd->bd->bi_enetaddr);
#endif

/* 如果有网卡设备,设置网卡MAC和IP地址 */
#if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)
	if (getenv ("ethaddr")) {
		smc_set_mac_addr(gd->bd->bi_enetaddr);
	}
#endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */

	/* Initialize from environment 通过环境变量初始化 */
	if ((s = getenv ("loadaddr")) != NULL) {
		load_addr = simple_strtoul (s, NULL, 16);
	}
#if (CONFIG_COMMANDS & CFG_CMD_NET)
	if ((s = getenv ("bootfile")) != NULL) {
		copy_filename (BootFile, s, sizeof (BootFile));
	}
#endif	/* CFG_CMD_NET */

#ifdef BOARD_LATE_INIT
	board_late_init ();		/* 该函数是开发板提供的,供不同的开发板做一些特有的初始化工作。 */
#endif

/*
 * 网卡初始化
 */
#if (CONFIG_COMMANDS & CFG_CMD_NET)
#if defined(CONFIG_NET_MULTI)
	puts ("Net:     ");
#endif
	eth_initialize(gd->bd);		/* 初始化以太网 */
#endif

    led_init();     /*led all off --forlinx add */

	/* 至此所有初始化工作已经完毕。main_loop在标准转入设备中接收命令行,然后分析,查找,执行 */
	/* main_loop() can return to retry autoboot, if so just run it again. main_loop()循环不断执行 */
	for (;;) {
		main_loop ();		/* main_loop()函数在common/main.c中定义 */
	}

	/* NOTREACHED - no way out of command loop except booting */
}

start_armboot()函数使用死循环调用main_loop()函数,作用是防止main_loop()函数开始的初始化代码如果调用失败后重新执行初始化操作,保证程序能进入到U-Boot的命令行。

main_loop()函数在common/main.c中。具体代码如下:

void main_loop (void)
{
#ifndef CFG_HUSH_PARSER
	static char lastcommand[CFG_CBSIZE] = { 0, };
	int len;
	int rc = 1;
	int flag;
#endif

#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
	char *s;
	int bootdelay;
#endif
#ifdef CONFIG_PREBOOT
	char *p;
#endif
#ifdef CONFIG_BOOTCOUNT_LIMIT
	unsigned long bootcount = 0;
	unsigned long bootlimit = 0;
	char *bcs;
	char bcs_set[16];
#endif /* CONFIG_BOOTCOUNT_LIMIT */

#if defined(CONFIG_VFD) && defined(VFD_TEST_LOGO)
	ulong bmp = 0;		/* default bitmap */
	extern int trab_vfd (ulong bitmap);

#ifdef CONFIG_MODEM_SUPPORT
	if (do_mdm_init)
		bmp = 1;	/* alternate bitmap */
#endif
	trab_vfd (bmp);
#endif	/* CONFIG_VFD && VFD_TEST_LOGO */

#ifdef CONFIG_BOOTCOUNT_LIMIT
	bootcount = bootcount_load();
	bootcount++;
	bootcount_store (bootcount);
	sprintf (bcs_set, "%lu", bootcount);
	setenv ("bootcount", bcs_set);
	bcs = getenv ("bootlimit");
	bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0;
#endif /* CONFIG_BOOTCOUNT_LIMIT */

#ifdef CONFIG_MODEM_SUPPORT
	debug ("DEBUG: main_loop:   do_mdm_init=%d\n", do_mdm_init);
	if (do_mdm_init) {
		char *str = strdup(getenv("mdm_cmd"));
		setenv ("preboot", str);  /* set or delete definition */
		if (str != NULL)
			free (str);
		mdm_init(); /* wait for modem connection */
	}
#endif  /* CONFIG_MODEM_SUPPORT */

#ifdef CONFIG_VERSION_VARIABLE
	{
		extern char version_string[];

		setenv ("ver", version_string);  /* set version variable */
	}
#endif /* CONFIG_VERSION_VARIABLE */

#ifdef CFG_HUSH_PARSER
	u_boot_hush_start ();
#endif

#ifdef CONFIG_AUTO_COMPLETE
	install_auto_complete();
#endif

#ifdef CONFIG_PREBOOT
	if ((p = getenv ("preboot")) != NULL) {
# ifdef CONFIG_AUTOBOOT_KEYED
		int prev = disable_ctrlc(1);	/* disable Control C checking */
# endif

# ifndef CFG_HUSH_PARSER
		run_command (p, 0);
# else
		parse_string_outer(p, FLAG_PARSE_SEMICOLON |
				    FLAG_EXIT_FROM_LOOP);
# endif

# ifdef CONFIG_AUTOBOOT_KEYED
		disable_ctrlc(prev);	/* restore Control C checking */
# endif
	}
#endif /* CONFIG_PREBOOT */

#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
	s = getenv ("bootdelay");
	bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;

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

# ifdef CONFIG_BOOT_RETRY_TIME
	init_cmd_timeout ();
# endif	/* CONFIG_BOOT_RETRY_TIME */

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

	debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");

	if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
# ifdef CONFIG_AUTOBOOT_KEYED
		int prev = disable_ctrlc(1);	/* disable Control C checking */
# endif

# ifndef CFG_HUSH_PARSER
		run_command (s, 0);
# else
		parse_string_outer(s, FLAG_PARSE_SEMICOLON |
				    FLAG_EXIT_FROM_LOOP);
# endif

# ifdef CONFIG_AUTOBOOT_KEYED
		disable_ctrlc(prev);	/* restore Control C checking */
# endif
	}

# ifdef CONFIG_MENUKEY
	if (menukey == CONFIG_MENUKEY) {
	    s = getenv("menucmd");
	    if (s) {
# ifndef CFG_HUSH_PARSER
		run_command (s, 0);
# else
		parse_string_outer(s, FLAG_PARSE_SEMICOLON |
				    FLAG_EXIT_FROM_LOOP);
# endif
	    }
	}
#endif /* CONFIG_MENUKEY */
#endif	/* CONFIG_BOOTDELAY */

#ifdef CONFIG_AMIGAONEG3SE
	{
	    extern void video_banner(void);
	    video_banner();
	}
#endif
#ifdef CONFIG_BOOT_MOVINAND
           ARMMenu();
#endif
#ifdef CONFIG_BOOT_NAND
           NAND_ARMMenu();
#endif
	/*
	 * Main Loop for Monitor Command Processing
	 */
#ifdef CFG_HUSH_PARSER
	parse_file_outer();
	/* This point is never reached */
	for (;;);
#else
	for (;;) {
#ifdef CONFIG_BOOT_RETRY_TIME
		if (rc >= 0) {
			/* Saw enough of a valid command to
			 * restart the timeout.
			 */
			reset_cmd_timeout();
		}
#endif
		len = readline (CFG_PROMPT);

		flag = 0;	/* assume no special flags for now */
		if (len > 0)
			strcpy (lastcommand, console_buffer);
		else if (len == 0)
			flag |= CMD_FLAG_REPEAT;
#ifdef CONFIG_BOOT_RETRY_TIME
		else if (len == -2) {
			/* -2 means timed out, retry autoboot
			 */
			puts ("\nTimed out waiting for command\n");
# ifdef CONFIG_RESET_TO_RETRY
			/* Reinit board to run initialization code again */
			do_reset (NULL, 0, 0, NULL);
# else
			return;		/* retry autoboot */
# endif
		}
#endif

		if (len == -1)
			puts ("<INTERRUPT>\n");
		else
			rc = run_command (lastcommand, flag);

		if (rc <= 0) {
			/* invalid command or not repeatable, forget it */
			lastcommand[0] = 0;
		}
	}
#endif /*CFG_HUSH_PARSER*/
}

main_loop()函数进行与具体平台无关的工作,主要包括初始化启动次数限制机制、设置软件版本号、打印启动信息、解析命令等。

用户不中断kernel引导的话,最后会调用run_command函数,它会解析输入的命令,然后根据命令的名字查找相应的函数进行调用。启动的时候需要调用两个命令:nand命令和bootm命令。

U-Boot如何给kernel传递参数?

U-Boot使用命令bootm来启动已经加载到内存中的内核。而bootm命令实际上调用的是bo_bootm函数。对于Linux内核,do_bootm函数会调用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);

	/*
	 * Check if there is an initrd image
	 */
	if (argc >= 3) {
		SHOW_BOOT_PROGRESS (9);

		addr = simple_strtoul (argv[2], NULL, 16);

		printf ("## Loading Ramdisk 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
			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)
		/*
		 *we need to copy the ramdisk to SRAM to let Linux boot
		 */
		memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
		data = ntohl(hdr->ih_load);
#endif /* CONFIG_B2 || CONFIG_EVB4510 */

		/*
		 * Now check if we have a multifile image
		 */
	} else if ((hdr->ih_type == IH_TYPE_MULTI) && (len_ptr[1])) {
		ulong tail = ntohl (len_ptr[0]) % 4;
		int i;

		SHOW_BOOT_PROGRESS (13);

		/* skip kernel length and terminator */
		data = (ulong) (&len_ptr[2]);
		/* skip any additional image length fields */
		for (i = 1; len_ptr[i]; ++i)
			data += 4;
		/* add kernel length, and align */
		data += ntohl (len_ptr[0]);
		if (tail) {
			data += 4 - tail;
		}

		len = ntohl (len_ptr[1]);

	} else {
		/*
		 * no initrd image
		 */
		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 %08lx) ...\n",
	       (ulong) theKernel);

#if defined (CONFIG_SETUP_MEMORY_TAGS) ||     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

	/* we assume that the kernel is in place */
	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);
}

Kernel如何读取U-Boot传递的相关参数呢?

对于Linux kernel,ARM平台启动时,先执行arch/arm/head.S,此文件会调用arch/arm/kernel/head-common.S中的函数,并最后调用start_kernel。

U-boot引导流程分析二

时间: 2024-10-07 22:17:21

U-boot引导流程分析二的相关文章

nova boot代码流程分析(二):nova-scheduler主机选择

本篇文章将分析nova-scheduler服务在创建VM时如何进行主机的选择.完整来说,nova-scheduler主机选择的过程主要分为以下几个阶段: 1. nova.scheduler.rpcapi.SchedulerAPI发出RPC请求到nova.scheduler.manager.SchedulerManager. 2. 从SchedulerManager到调度器(类SchedulerDriver). 3. 从SchedulerDriver到Filters. 4. 从Filters到权重

U-boot引导流程分析一

U-Boot,全称 Universal Boot Loader,即通用引导程序,是遵循GPL条款的开放源码项目.它的源码目录.编译形式与Linux内核很相似,事实上,不少U-Boot源码就是相应的Linux内核源程序的简化,尤其是一些设备的驱动程序,这从U-Boot源码的注释中能体现这一点.U-Boot不仅仅支持嵌入式Linux系统的引导,它还支持NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS嵌入式操作系统. U-Boot的工作模式有启动加载模式和下载模式.

nova boot代码流程分析(三):nova与neutron的交互(2)

继续<nova boot代码流程分析(三):nova与neutron的交互(1)>的分析. #/nova/virt/libvirt/driver.py:LibvirtDriver # NOTE(ilyaalekseyev): Implementation like in multinics # for xenapi(tr3buchet) def spawn(self, context, instance, image_meta, injected_files, admin_password,

Android 4.4 音量调节流程分析(二)

之前在Android 4.4 音量调节流程分析(一)里已经有简单的分析音量控制的流程,今天想接着继续分析下音量大小计算的方法.对于任一播放文件而言其本身都有着固定大小的音量Volume_Max,而在AudioPolicyManagerBase.cpp文件中音量调节可以理解为在Volume_Max的基础上乘以系数κ(0≤κ≤1). 现在对AudioPolicyManagerBase.cpp中volIndexToAmpl函数做具体分析,volIndexToAmpl的函数定义如下: 1 float A

nova boot代码流程分析(五):VM启动从neutron-dhcp-agent获取IP与MAC

1.   network和subnet创建代码流程 [[email protected] ~(keystone_user1)]# neutron net-create demo-net [[email protected] ~(keystone_user1)]# neutron subnet-create  demo-net 1.1.1.0/24 --name demo-subnet --gateway 1.1.1.1 --enable_dhcp true 这里,我们主要分析上面两个命令的代码流

nova boot代码流程分析(一):Claim机制

nova boot创建VM的流程大致为: 1. novaclient发送HTTP请求到nova-api(这里内部细节包括keystone对用户的验证及用户从keystone获取token和endpoints等信息,具体参考<keystone WSGI流程>). 2. nova-api通过rpc调用到nova-conductor. 3. nova-conductor调用rpc进入nova-scheduler进行compute节点的选择,nova-scheduler将compute节点选择的信息的

Android启动流程分析(二) init进程的启动

############################################# 本文为极度寒冰原创,转载请注明出处 ############################################# 分析init进程,首先要分析init进程是如何启动的, init的源码位于(system/core/init),我们先来看看init进程的android.mk 下面是从system/core/init模块拿出来的一段code: LOCAL_MODULE:= init LOCAL_

iOS学习之项目流程分析(二)

今天我们来给昨天的Demo加上下拉刷新和上拉加载更多的功能. 1.下拉刷新. 在viewDidLoad中调用方法addRefreshControl,下拉时可以出现风火轮加载更多的效果. - (void)addRefreshControl { self.refreshControl = [[[UIRefreshControl alloc] initWithFrame:CGRectZero] autorelease]; //frame不用给,系统默认有. self.refreshControl.at

HBase1.0.0源码分析之请求处理流程分析以Put操作为例(二)

HBase1.0.0源码分析之请求处理流程分析以Put操作为例(二) 1.通过mutate(put)操作,将单个put操作添加到缓冲操作中,这些缓冲操作其实就是Put的父类的一个List的集合.如下: private List<Row> writeAsyncBuffer = new LinkedList<>(); writeAsyncBuffer.add(m); 当writeAsyncBuffer满了之后或者是人为的调用backgroundFlushCommits操作促使缓冲池中的