1. 程式人生 > >u-boot、kernel和filesystem 執行過程分析

u-boot、kernel和filesystem 執行過程分析

標題:

  Uboot -kerne-root 啟動流程

內容:

  ※uboot啟動流程

  ※Kernel啟動流程

  ※Root啟動流程

  ※構建根檔案系統

 

/*********************************

*u-boot:   u-boot2012.04.01

*kernel:    linux-2.6.22

*busybox:    busybox-1.1.6

*********************************/ 

 

一、uboot啟動流程

1.初始化硬體
2.把核心從NAND讀到SDRAM
3.設定核心啟動引數
4.跳轉執行核心
 
執行第一個程式碼:/cpu/arm920t/start.S
 
 
第一階段(start.S)
reset:
   set the cpu to SVC32 mode          //管理模式
   turn off the watchdog               //關看門狗
   mask all IRQs                         //遮蔽中斷
   cpu_init_crit
  flush v4 I/D caches              //關閉caches
  disable MMU stuff and caches     //關閉MMU
  lowlevel_init  //配置SDRAM
   Relocate                          //重定位
   stack_setup:                       //設定堆疊
   clear_bss:                         //清楚bss段
   start_armboot                      //跳轉執行第二階段

第二階段(board.c)

start_armboot (void)
  init_sequence
  cpu_init,                      /* basic cpu dependent setup */
  board_init,                   /* basic board dependent setup */
  interrupt_init,              /* set up exceptions */
  env_init,                      /* initialize environment */
  init_baudrate,               /* initialze baudrate settings */
  serial_init,                   /* serial communications setup */
  console_init_f,             /* stage 1 init of console */
  display_banner,                        /* say that we are here */
  flash_init            初始化NOR
  nand_init()           初始化NAND
  env_relocate ()        將環境變數讀入指定位置
  cs8900_get_enetaddr     初始化網路裝置
  main_loop ()            死迴圈
    s = getenv ("bootdelay")
      bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
    s = getenv ("bootcmd");
    Bootcmd=nand read.jffs2 0x30007fc0 kernel;bootm 0x30007fc0  //環境變數
    run_command (s, 0);
    continue;

Uboot命令的實現

①串列埠輸入命令(字串)

②動作(函式),命令對應於名字

因而根據命令找到對應函式,來執行。Uboot裡面有一個結構體

包含1.名

2.函式

struct cmd_tbl_s {
          char                  *name;              /* Command Name                                 */
          int                    maxargs;           /* maximum number of arguments           */
          int                    repeatable;         /* autorepeat allowed?                */
                                                          /* Implementation function         */
          int                    (*cmd)(struct cmd_tbl_s *, int, int, char *[]);
          char                  *usage;              /* Usage message           (short)   */
#ifdef  CFG_LONGHELP
          char                  *help;               /* Help  message            (long)   */
#endif
#ifdef CONFIG_AUTO_COMPLETE
          /* do auto completion on the arguments */
          int                    (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
#endif
};

此時run_command函式根據名字匹配該結構體

Run_command
命令的提取
while (*str) {
 
                      /*
                       * Find separator, or string end
                       * Allow simple escape of ';' by writing "\;"
                       */
                      for (inquotes = 0, sep = str; *sep; sep++) {
                                  if ((*sep=='\'') &&
                                      (*(sep-1) != '\\'))
                                              inquotes=!inquotes;
 
                                  if (!inquotes &&
                                      (*sep == ';') &&         /* separator                    */
                                      ( sep != str) &&         /* past string start           */
                                      (*(sep-1) != '\\'))        /* and NOT escaped        */
                                              break;
                      }
parse_line()   //解析命令
Cmdtp=find_cmd()      //匹配命令  cmdtp就是指向上面的結構體cmd_tbl_s
for (cmdtp = &__u_boot_cmd_start;
               cmdtp != &__u_boot_cmd_end;
               cmdtp++)

以上:

#define Struct_Section  __attribute__ ((unused,section (".u_boot_cmd")))

比如啟動命令:

Bootcmd=nand read.jffs2 0x30007fc0 kernel;bootm 0x30007fc0以bootm 0x30007fc0為例
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"
#ifdef CONFIG_OF_FLAT_TREE
     "\tWhen booting a Linux kernel which requires a flat device-tree\n"
     "\ta third argument is required which is the address of the of the\n"
     "\tdevice-tree blob. To boot that kernel without an initrd image,\n"
     "\tuse a '-' for the second argument. If you do not pass a third\n"
     "\ta bd_info struct will be passed instead\n"
#endif
);

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}

以上可以展開為

cmd_tbl_t __u_boot_cmd_bootm  __attribute__ ((unused,section (".u_boot_cmd")))
= {bootm, CFG_MAXARGS, 1, do_bootm, "bootm   - boot application image from memory\n", help}

以上解釋為:bootm命令定義了__u_boot_cmd_bootm結構體,該結構體型別是cmd_tbl_t,並且強制為.u_boot_cmd段屬性

 

Uboot啟動核心

核心啟動依賴nand read.jffs2 0x30007fc0 kernel;bootm 0x30007fc0

nand read.jffs2 0x30007fc0 kernel把核心讀到0x30007fc0

從哪裡讀? --kernel分割槽在配置檔案裡面寫死了smdk2410.h

讀到哪裡? --0x30007fc0

Nand命令分析:

"nand read[.jffs2]     - addr off|partition size\n"

 

bootm 0x30007fc0從bootm 0x30007fc0啟動它

Bootm命令分析:

do_bootm
image_header_t *hdr = &header;
typedef struct image_header {
   uint32_t            ih_magic;          /* Image Header Magic Number  */
   uint32_t            ih_hcrc; /* Image Header CRC Checksum */
   uint32_t            ih_time;            /* Image Creation Timestamp      */
   uint32_t            ih_size; /* Image Data Size                     */
   uint32_t            ih_load; /* Data  Load  Address               */
   uint32_t            ih_ep;               /* Entry Point Address                */
   uint32_t            ih_dcrc; /* Image Data CRC Checksum     */
   uint8_t              ih_os;                /* Operating System                   */
   uint8_t              ih_arch; /* CPU architecture                    */
   uint8_t              ih_type; /* Image Type                            */
   uint8_t              ih_comp;           /* Compression Type                  */
   uint8_t              ih_name[IH_NMLEN];   /* Image Name              */
} image_header_t;
memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);

//如果當前地址不是入口地址,移到載入地址0x30008000,由於頭部佔用64位元組,所以此時地址是0x30007fc0
do_bootm_linux  (cmdtp, flag, argc, argv,
                                       addr, len_ptr, verify); //啟動核心
setup_start_tag (bd);
setup_memory_tags (bd);
setup_commandline_tag (bd, commandline);
setup_end_tag (bd);
printf ("\nStarting kernel ...\n\n");
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);

二、Kernel啟動流程

處理uboot傳入的引數

①判斷是否支援這個CPU

②判斷是否支援這個單板

③建立頁表

④使能MMU

⑤跳到start_kernel函式

 

核心執行的第一個程式碼檔案:/arch/arm/kernel/head.S  和 /arch/arm/boot/bootp/init.S

 

Start_kernel分析

1.輸出核心版本資訊

2.Setup_arch($command_line)

3.Setup_command_line(command_line)

 

呼叫關係:

Start_kernel
            printk(linux_banner);   //輸出版本資訊
            setup_arch(&command_line); //匹配ID,arch-type
                        setup_processor();
                                    list = lookup_processor_type(processor_id);
                        mdesc = setup_machine(machine_arch_type);
parse_cmdline(cmdline_p, from); //解析命令
            setup_command_line(command_line);
                        strcpy (saved_command_line, boot_command_line);
Rest_init
  Kernel_thread()    //執行緒
  // kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
    kernel_init()
    Prepare_namespace()
    Mount_root()
    Init_post()
      Run_init_process()  //執行檔案系統第一個程式
      ...

三、Root啟動流程

init過程

1.讀取配置檔案

2.解析配置檔案

3.執行客戶程式

 

 

配置檔案:1.指定程式;2.何時執行

 

busybox呼叫過程

busybox -> init_main
               parse_inittab
                           file = fopen(INITTAB, "r");//開啟配置檔案/etc/inittab
                          (沒有配置檔案時使用預設配置檔案)
                           new_init_action()
                           #1.建立init_action結構,填充
                           #2.把這個結構放入init_action_list連結串列裡面
               run_actions(SYSINIT);
                           waitfor(a, 0); //執行程式,等待執行完畢
                                       run(a); //系統呼叫,建立precess子程序
                                       waitpid(runpid, &status, 0) //等待結束
                           delete_init_action(a);//在init_action_list裡面刪掉應用程式
               run_actions(WAIT);
                           waitfor(a, 0);
                           delete_init_action(a);
               run_actions(ONCE);
                           run(a);//不會等待子程序結束
                           delete_init_action(a);//在init_action_list裡面刪
               while (1) {
                     run_actions(RESPAWN);
                                       run(a)  
                           run_actions(ASKFIRST);
                                       run(a)
                                                   列印:"\nPlease press Enter to activate this console. ";
                                                   等待回車
                                                   建立子程序
                            wpid = wait(NULL);//等待子程序退出
                            while (wpid > 0) {
                            a->pid = 0;//退出後,設定pid=0
                           }
                       }

init_action_list裡面有id,action,process等等

 

 

 static void new_init_action(int action, const char *command, const char *cons)

struct init_action *new_action, *a, *last;

 

init_action裡面有:

struct init_action *next;

   int action;

   pid_t pid;    //程序號

   char command[INIT_BUFFS_SIZE];  //應用程式

   char terminal[CONSOLE_NAME_SIZE];  //終端

 

 

new_init_action做了什麼

1.建立init_action結構,填充

2.把這個結構放入init_action_list連結串列裡面          

 

現在假設沒有配置檔案,根據

/* No inittab file -- set up some default behavior */
#endif
               /* Reboot on Ctrl-Alt-Del */
               new_init_action(CTRLALTDEL, "reboot", "");
               /* Umount all filesystems on halt/reboot */
               new_init_action(SHUTDOWN, "umount -a -r", "");
               /* Swapoff on halt/reboot */
               if (ENABLE_SWAPONOFF) new_init_action(SHUTDOWN, "swapoff -a", "");
               /* Prepare to restart init when a HUP is received */
               new_init_action(RESTART, "init", "");
               /* Askfirst shell on tty1-4 */
               new_init_action(ASKFIRST, bb_default_login_shell, "");
               new_init_action(ASKFIRST, bb_default_login_shell, VC_2);
               new_init_action(ASKFIRST, bb_default_login_shell, VC_3);
               new_init_action(ASKFIRST, bb_default_login_shell, VC_4);
               /* sysinit */
               new_init_action(SYSINIT, INIT_SCRIPT, "");

來反推出預設配置項

#inittab格式:(檔案在example/inittab檔案)

 #<id>:<runlevels>:<action>:<process>

 

 #<id>         :/dev/id,最終用作終端:stdin,stdout,stderrer:printf,scanf,err

 #<runlevels>  :忽略

 #<action>     :何時執行的

 <action>: Valid actions include: sysinit, respawn, askfirst, wait, once,

                                  restart, ctrlaltdel, and shutdown.

 

 #<process>    :應用程式或者指令碼

new_init_action(ASKFIRST, -/bin/sh, /dev/tty2);    

 

 

 new_init_action(CTRLALTDEL, "reboot", "");

 id=null

 runlevels=null

 action=ctrlaltdel

 那麼上面就是:

 ::ctrlaltdel:reboot

 同理可知其他的配置項是:

 ::shutdown:umount -a -r

 ::restart:init

 ::askfirst:-/bin/sh

 tty2:askfirst:-/bin/sh

 tty3:askfirst:-/bin/sh

 tty4:askfirst:-/bin/sh

 ::sysinit:/etc/init.d/rcS

 

其實:

 main裡面

 signal(SIGHUP, exec_signal);

   signal(SIGQUIT, exec_signal);

   signal(SIGUSR1, shutdown_signal);

   signal(SIGUSR2, shutdown_signal);

   signal(SIGINT, ctrlaltdel_signal);

   signal(SIGTERM, shutdown_signal);

   signal(SIGCONT, cont_handler);

   signal(SIGSTOP, stop_handler);

   signal(SIGTSTP, stop_handler);

      

當用戶按下 ctrl + alt +del時會產生一個訊號,然後執行ctrlaltdel_signal函式

static void ctrlaltdel_signal(int sig ATTRIBUTE_UNUSED)

{

   run_actions(CTRLALTDEL);

}

總結:應用程式所需要的檔案

 

1./dev/console /dev/null

2.配置檔案/etc/inittab

3.配置檔案裡面指定的應用程式

4.庫檔案

5.init本身,即busybox

以上就是最小根檔案系統所需要的項