arm linux 啟動流程之 ppcboot

來源:互聯網
上載者:User

Author-------Dansen-----xzd2734@163.com

不是每一行代碼都必須讀懂,我只是大概地過一下流程
畢竟這些都是比較成熟的代碼,沒必要去改的
是針對我自己的板子的,硬體設定如下
cpu是s3c2410
board type 是 smdk2410
16M Nor Flash 地址是 0x0---0xFFFFFF
64M SDRAM     地址是 0x30000000---0x33FFFFFF
軟體是華恒版的
ppcboot 2.0 和 linux 2.4.18
仔細分析了一下啟動的流程,能更好地理解硬體和軟體的配合
方便移植。
我們在flash的開始處燒寫了ppcboot.bin,這是可執行檔二進位檔案
注意和ELF可執行性檔案是有區別的。
cpu上電後可以從直接從flash地址0處取指令來執行
開始的代碼在ppcboot-2.0.0/cpu/arm920t/start.s中
這裡需要提一下編譯連結時用到的一個很重要的連結檔案
ppcboot-2.0.0/board/smdk2410/ppcboot.lds
這個檔案給出了代碼中各標號的基地址,和各個段的連結順序
ENTRY(_start)
SECTIONS
{
        . = 0x00000000;

        . = ALIGN(4);
 .text      :
 {
   cpu/arm920t/start.o (.text)
   *(.text)
 }

        . = ALIGN(4);
        .rodata : { *(.rodata) }

        . = ALIGN(4);
        .data : { *(.data) }

        . = ALIGN(4);
        .got : { *(.got) }

 armboot_end_data = .;

        . = ALIGN(4);
        .bss : { *(.bss) }

 armboot_end = .;
}
可以看到程式的入口是_start標號指示的,而cpu/arm920t/start.o
則被安排在程式最開始的地方,這個標號就是在start.s中
但是還有一點是需要特別注意的,開始我也是因為這個地方而沒有很好地理解程式
雖然lds中有 . = 0x00000000 這一句,指示連結基地址,不過其實這句是不起作用的,
真正的連結基地址在ppcboot-2.0.0/config.mk中指定的
LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE)
其中-Ttext $(TEXT_BASE)就是指定連結地址為TEXT_BASE的值
因而是可變的,TEXT_BASE在ppcboot-2.0.0/board/smdk2410/config.mk中定義
TEXT_BASE = 0x33F00000
ppcboot-2.0.0/config.mk是包括到Makefile中的,
在Makefile中有$(LD) $(LDFLAGS) $(OBJS) $(LIBS) $(LIBS) -Map ppcboot.map -o ppcboot
所以說起來真正的連結地址是0x33F00000,其實這樣在把ppcboot拷到Ram中就可以實現無縫跳轉了
.globl _start
_start: b       reset
跳到renset
reset: ldr     r0, =pWTCON
 mov     r1, #0x0
 str     r1, [r0]
..........................
 bl cpu_init_crit  //bl跳轉會回來
relocate: //下面開始要把ppcboot拷到Ram中
 adr r0, _start  /* r0 <- current position of code */
 ldr r2, _armboot_start
 ldr r3, _armboot_end
 sub r2, r3, r2  /* r2 <- size of armboot */
 ldr r1, _TEXT_BASE  /* r1 <- destination address */
 add r2, r0, r2  /* r2 <- source end address */
以上代碼需要注意的一點是 adr 和 ldr 的區別
adr取得是當前pc相關的位移地址,在這裡程式還是在flash中運行
所以取得地址是以0x0為基址的
而ldr取的是_armboot_start所指的值
.globl _armboot_start
_armboot_start:
 .word _start
看到它的值也是_start的地址,不過我們這裡取的是絕對位址,是在連結是確定的以
TEXT_BASE為基址的.由於_start的位移是0,所以r0是0,r2就是TEXT_BASE
copy_loop:
 ldmia r0!, {r3-r10}
 stmia r1!, {r3-r10}
 cmp r0, r2
 ble copy_loop
迴圈copy
 ldr r0, _armboot_end  /* set up the stack */
 add r0, r0, #CONFIG_STACKSIZE
 sub sp, r0, #12  /* leave 3 words for abort-stack */

 ldr pc, _start_armboot 
_start_armboot: .word start_armboot

//通過這一句跳轉到ppcboot-2.0.0/lib_arm/board.c中的start_armboot函數去執行了
start_armboot的絕對位址也是以TEXT_BASE為基址的,所以可以順利的實現無縫跳轉了.
接著下來就是一系列初始化的工作了
首先定義了一個全域的資料結構 gd_t gd_data;
DECLARE_GLOBAL_DATA_PTR 這個宏定義的是一個全域的gd_t類型的指標gd
gd = &gd_data;
這樣以後就可以用gd來訪問gd_data這個資料結構了
 for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
  if ((*init_fnc_ptr)() != 0) {
   hang ();
  }
 }
init_fnc_ptr中是一系列初始化函數的指標
init_fnc_t *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 */
 display_banner,
 dram_init,  /* configure available RAM banks */
 display_dram_config,

 NULL,
};
基本上serial_init後我們就可以用printf函數來列印資訊了.
 for (;;) {
  main_loop ();
 }
進入了主迴圈,在M:/ppcboot-2.0.0/common/main.c中
 {
  char c = 'y';
  unsigned long timedata;
  printf("start linux now(y/n):");
  timedata = 0;
  for (;;) {
   while (!tstc()) { /* while no incoming data */
    if (timedata++ > 3000 * 100 *3)
     goto bootm; /* timed out */
   }
  c = getc();
  }
tstc()是測試串口是否有資料輸入,顯然沒有的話就會等待time out跳出
bootm:
  if(c == 'y'||c == 'Y'){
   strcpy(lastcommand , "bootm 30008000 30800000/r");
   flag = 0;
   rc = run_command (lastcommand, flag);
   if (rc <= 0) {
    /* invalid command or not repeatable, forget it */
    lastcommand[0] = 0;
   }
  }
  else{
   printf("/n/n");
  }
 } 
這樣如果串口沒有輸入或者輸入時y Y 的話,就會去執行bootm 30008000 30800000/r這條命令
否則就會到ppcboot的命令列等待輸入.
執行bootm 30008000 30800000/r這條命令會調用ppcboot-2.0.0/common/cmd_bootm.c中的
do_bootm函數,具體的命令怎樣被分解,選擇調用函數的機制我就不多說了,追著run_command去就是了
在do_bootm中調用了do_bootm_linux函數,這個函數在ppcboot-2.0.0/lib_arm/armlinux.c中
    ret = memcpy((void *)0x30008000, (void *)0x40000, 0x100000);
    if (ret != (void *)0x30008000)
     printf("copy kernel failed/n");
    else
    printf("copy kernel done/n");     

    ret = memcpy((void *)0x30800000, (void *)0x140000, 0x440000);
    if (ret != (void *)0x30800000)
              printf("haha failed/n");
    else
              printf("copy ramdisk done/n");

首先把kernel和ramdisk都拷到Ram相應的地方去.
    setup_linux_param(0x30000000 + LINUX_PARAM_OFFSET); //也在armlinux.c中
    #define LINUX_PARAM_OFFSET 0x100
建立要傳給核心的參數,參數的地址都是固定的,所以核心也知道去這裡取參數
參數格式比較複雜,我這裡好像傳得參數不多
void setup_linux_param(ulong param_base)
{
 struct param_struct *params = (struct param_struct *)param_base;
...............
}
只是通過一個param_struct的結構體來傳參數的,不過現在一般都用另一種tag標記的傳參方法
一個主要的參數時char linux_cmd[] = "initrd=0x30800000,0x440000  root=/dev/ram init=/linuxrc console=ttyS0";
 if (linux_cmd == NULL) {
  printf("Wrong magic: could not found linux command line/n");
 } else {
  memcpy(params->commandline, linux_cmd, strlen(linux_cmd) + 1);
  printf("linux command line is: /"%s/"/n", linux_cmd);
 }
是比較重要的.移植的時候常常需要修改
接著call_linux(0, 0xc1, 0x30008000); 看出來是準備調到linux去了
0xc1是machine type,這三個參數分別給了r0,r1,r2,這些都是調用核心的約定
void  call_linux(long a0, long a1, long a2)
{
__asm__(
 "mov r0, %0/n"
 "mov r1, %1/n"
 "mov r2, %2/n"
 "mov ip, #0/n"
 "mcr p15, 0, ip, c13, c0, 0/n" /* zero PID */
 "mcr p15, 0, ip, c7, c7, 0/n" /* invalidate I,D caches */
 "mcr p15, 0, ip, c7, c10, 4/n" /* drain write buffer */
 "mcr p15, 0, ip, c8, c7, 0/n" /* invalidate I,D TLBs */
 "mrc p15, 0, ip, c1, c0, 0/n" /* get control register */
 "bic ip, ip, #0x0001/n"  /* disable MMU */
 "mcr p15, 0, ip, c1, c0, 0/n" /* write control register */
 "mov pc, r2/n"
 "nop/n"
 "nop/n"
 : /* no outpus */
 : "r" (a0), "r" (a1), "r" (a2)
 );
}
mov pc, r2 就是這句吧,調到了30008000去執行核心了

接下來就到核心了吧 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.