U-boot分析與移植(3)----U-boot stage2分析 .

 .

一來到void start_armboot (void)函數,馬上出現兩個很重要的數據結構gd_t和bd_t

1、gd_t : global data數據結構定義,位於文件 include/asm-arm/global_data.h。其成員主要是一些全局的系統初始化參數。

typedef	struct	global_data {
	bd_t		*bd;      // struct board_info指針,保存板子信息
	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;

2.、bd_t :board info數據結構定義,位於文件 include/asm-arm/u-boot.h。保存板子參數。

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  板子ID*/
    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;


分配一個存儲全局數據的區域,地 址給指針 gd

gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));


清0並分配空間

memset ((void*)gd, 0, sizeof (gd_t));  


在gd前面的位置給 gd->bd賦值地址

 gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));


清0並分配空間

 memset (gd->bd, 0, sizeof (bd_t));


執行一系列初始化函數

for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
  if ((*init_fnc_ptr)() != 0) {
   hang ();
  }
 }


假如函數指針指向的函數返回值不爲0,那麼在hang()裏就會死循環,初始化失敗

void hang (void)
{
 puts ("### ERROR ### Please RESET the board ###\n");
 for (;;);
}


函數列表如下:

每個初始化函數正常情況下返回值是0

init_fnc_t *init_sequence[] = {
 cpu_init,  /* 初始化irq/fiq模式的棧*/
 board_init, /* 設置系統時鐘*/
 interrupt_init, /*初始化定時器*/
 env_init,  /* 檢查flash上的環境參數是否有效*/
 init_baudrate, /* 初始化波特率*/
 serial_init, /* 初始化串口*/
 console_init_f, /*初始化串口控制檯*/
 display_banner, /* say that we are here */

接着進行一些NOR FLASH,LCD,串口,控制檯,sd卡,網卡等初始化,不一一列舉了。

終於來到重要的時刻了 - -#

進入一個死循環

 for (;;)
 {
  main_loop ();
 }


繼續跟蹤

發現在bootdelay時間內按下鍵進入命令行,用run_command來解析命令

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

如果CONFIG_BOOTDELAY已經定義,用s得到環境變量bootdelay,然後倒數啓動內核

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


CONFIG_BOOTCOUNT_LIMIT是設置u-boot啓動次數的限制

最後s = getenv ("bootcmd");獲得啓動參數

		run_command (s, 0);

啓動命令解析
在run_command 函數裏最終執行命令

  /* OK - call function to do the command */
  if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {
   rc = -1;
  }

這是一個命令結構體,原型如下:

struct cmd_tbl_s {
	char		*name;		/* Command Name			*/
	int		maxargs;	         /* 最大的參數個數	*/
	int		repeatable;	/* 命令可否重複	*/
	int		(*cmd)(struct cmd_tbl_s *, int, int, char *[]);/*對應的函數指針*/
	char		*usage;		/* Usage message	(short)	*/

正常情況下就會執行U_BOOT_CMD命令,U_BOOT_CMD宏定義一個命令,命令宏原型如下:

/*命令宏U_BOOT_CMD*/
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t    __u_boot_cmd_##name     Struct_Section = {#name, maxargs, rep, cmd, usage, help}

假若上面是傳入的是一個bootm命令啓動內核,將會調用相應的

 U_BOOT_CMD裏的do_bootm函數
U_BOOT_CMD(
 	bootm,	CFG_MAXARGS,	1,	do_bootm,
 	"bootm   - boot application image from memory\n",
 	"[addr [arg ...]]\n    - boot application image stored in memory\n"
 	"\tpassing arguments 'arg ...'; when booting a Linux kernel,\n"
 	"\t'arg' can be the address of an initrd image\n"

在do_bootm函數裏,將用switch case檢查內核zImage類型,解壓方式,操作系統等,因爲zImage是自解壓的,不用解壓

switch (hdr->ih_os) {
	default:			/* handled by (original) Linux case */
	case IH_OS_LINUX:

	    do_bootm_linux  (cmdtp, flag, argc, argv,
			     addr, len_ptr, verify);
	    break;	

最後,將進入Armlinux.c的do_bootm_linux函數啓動Linux內核

U_Boot也是通過標記列表向內核傳遞參數的

#ifdef CONFIG_CMDLINE_TAG
	char *commandline = getenv ("bootargs");
#endif

CONFIG_CMDLINE_TAG在smdk2410.h裏已經定義了

theKernel指向內核 存放的地址,(對於ARM架構的CPU,通常是0x30008000),

/*聲明內核的入口函數指針*/

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

 

/*把內核入口地址賦值給theKernel,hdr是image_header_t結構體,指向uImage頭部 ,ih_ep是內核的入口點(Entry Point)*/

theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep); 

/*最後是對內核入口函數的調用,bd->bi_arch_number是這個板子機器類型ID, bd->bi_boot_params是傳給內核的參數,從標記列表地址開始*/

theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
引導Linux內核啓動的必須要滿足的幾個條件:
* CPU register settings //這裏也就是我們的theKernel中的作用
          o r0 = 0.
          o r1 = machine type number.
          o r2 = physical address of tagged list in system RAM.
    * CPU mode
          o All forms of interrupts must be disabled (IRQs and FIQs.)
          o The CPU must be in SVC mode. (A special exception exists for Angel.)
    * Caches, MMUs
          o The MMU must be off.
          o Instruction cache may be on or off.
          o Data cache must be off and must not contain any stale data.
    * Devices
          o DMA to/from devices should be quiesced.
    * The boot loader is expected to call the kernel image by jumping directly to the first instruction of the kernel image.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章