Bootloader - Main system - Recovery的三角關係

一、MTD分區:
BOOT:        boot.img,Linux kernel (within normal ramdisk)
MISC:        bootloader message struct
RECOVERY:    recovery.img,Linux kernel (within recovery ramdisk)
SYSTEM:      system.img
DATA:        userdata.img
CACHE:       some cache files


二、 三個部分、兩個接口
   Recovery的工作需要整個軟件平臺的配合,從架構角度看,有三個部分
       1) Main system:用boot.img啓動的Linux系統,Android的正常工作模式。

       2) Recovery:用recovery.img啓動的Linux系統,主要是運行Recovery程序。

       3) Bootloader:除了加載、啓動系統,還會通過讀取flash的MISC分區獲得來自Main system和Recovery的消息,並以此決定做何種操作。

      在Recovery的工作流程中,上述三個實體的通信必不可少。通信的接口有以下兩個:

1. 接口1: CACHE分區中的三個文件:/cache/recovery/…

     Recovery通過/cache/recovery裏的文件與main system通信,有三個文件:
     1) /cache/recovery/command (Main System-->Recovery)
         Main system傳給Recovery的命令行,每一行有一個命令,支持以下幾種:
            --send_intent=anystring - write the text out to recovery.intent
            --update_package=path - verify install an OTA package file
            --wipe_data - erase user data (and cache), then reboot
            --wipe_cache - wipe cache (but not user data), then reboot
            --set_encrypted_filesystem=on|off - enables / diasables encrypted fs

     2) /cache/recovery/log(Recovery寫的log文件)
         Recovery的log輸出,在recovery運行過程中,stdout及stderr會重定位到/tmp/recovery.log文件,Recovery退出之前會將其轉儲到/cache/recovery/log中,也就是cache分區的recovery/log。

     3) /cache/recovery/intent(Recovery-->Main system)
        Recovery傳給Main system的信息

2. 接口2: BCB (bootloader control block)即bootloader_message

  1. struct bootloader_message {  
  2.     char command[32];  
  3.     char status[32];  
  4.     char recovery[1024];  
  5. };  

       BCB是Bootloader與Recovery的通信接口,也是Bootloader與Main system的通信接口,存儲在flash中的MISC分區,佔用三個page,各成員意義如下:

      • command:
      當Main system(Linux)想要重啓進入recovery模式,或升級radio/bootloader firmware時,會更新這個域。當firmware更新完畢,爲了啓動後進入recovery做最終的清除,bootloader還會修改它。

      •status:
      update-radio或update-hboot完成後,bootloader會寫入相應的信息,一般是一些狀態或執行結果。

      • recovery:
      僅被Main system(Linux)寫入,用於向Recovery發送消息,必須以“recovery\n”開頭,否則這個域的所有內容會被忽略。這一項的內容中“recovery/\n”以後的部分,是/cache/recovery/command支持的命令,可以認爲這是在Recovery操作過程中,對命令操作的備份。Recovery也會更新這個域的信息,執行某操作前把該操作命令寫到recovery域,並更新command域,操作完成後再清空recovery域及command域,這樣在進入Main system之前,就能確保操作被執行。

      如下圖所示,Main system、Recovery與Bootloader通過上述接口通信,通信邏輯依不同的目的而不同,在後面介紹具體工作流程中還會詳細介紹。

三、從Main system進入Recovery的方法
       從Main system進入到Recovery,要修改misc分區的數據並重啓,從而告訴Bootloader是用boot.img還是用recovery.img啓動。

      /system/core/init/signal_handler.c裏的wait_for_one_process函數中有如下代碼:

      android_reboot(ANDROID_RB_RESTART2, 0, "recovery")->

        __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,

                       LINUX_REBOOT_CMD_RESTART2, “recovery”);

      一些關鍵的進程運行異常,會重啓進入recovery模式,這裏用__reboot函數進入recovery。跟蹤這個函數,由系統調用處理函數,到kernel_restart(char *cmd),最終調用machine_restart使用體系結構相關的代碼完成重啓。

       Android中沒有給出如何處理“recovery”重啓。不過可以斷定,在重啓之前會向BCB中寫入信息,以告知bootloader如何啓動,具體操作是這樣的:

        向command域中寫入“boot-recovery”      // 此操作必做

        向recovery域寫入“recovery\n”         // 此操作也可不做

       這些操作很可能在kernel_restart(char *cmd)中完成,因爲這一部分與體系結構無關,如果要實現完整的Recovery,這部分工作是必須做的。

       Bootloader得到進入Recovery模式的指示,用recovery.img啓動,進入Recovery模式,init.rc (bootable/recovery/etc/init.rc)的內容比Main system的要短的多,最重要的是把recovery程序作爲服務啓動:

service recovery /sbin/recovery

四、Android Recovery: 總體流程
      根據Recovery的initrc,kernel啓動完成後,啓動recovery服務,這是一個C程序,入口在/bootable/recovery/recovery.c中,main函數結構清晰,主要流程如圖:

 1. get_args

    首先調用get_args獲取參數,主要流程如下:

      get_args不僅傳回獲取到的參數,還會將其寫入misc分區中的BCB,這樣,一旦升級或擦除數據的過程中出現錯誤,重啓之後依然進入Recovery並做相同操作。

2. install_package

    或install_rkimage

    或wipe_data

    或wipe_cache

3. finish_recovery

    離開Recovery進入Main system的必經之路,流程如下:

• intent內容作爲參數傳進來,如果有intent需要告知Main system,將其寫入/cache/recovery/intent;
• 將所有log信息轉儲到/cache/recovery/log文件,以供Main system讀取;
• 清除BCB,也就是告知Bootloader啓動進入Main system;
• 刪除/cache/recovery/command;
以上是整體流程中的幾個函數,關於安裝升級包、升級firmware等操作將在具體流程中介紹。

五、Android Recovery: Factory data reset流程
      如果系統不穩定,可以嘗試恢復出廠設置,該操作會擦除DATA分區及CACHE分區,有兩種恢復方式,下面分別介紹:
1. 通過Setting程序發起Facory data reset
 

 

        1) 在應用程序Setting中選擇factory data reset

     2) Main system向/cache/recovery/command寫入”–wipe_data”

     3) Main system重啓進入recovery模式(方法:修改BCB)

     4) Recovery向BCB寫入”boot-recevory”和”recovery\n–wipe_data\n”

     5) 擦除DATA分區,裏面是用戶數據,擦除CACHE分區

     6) finish_recovery函數

     7) 重啓,回到Main system

     第4) 步驟,Recovery向BCB寫入boot-recovery和–wipe_data,這是爲了保證後面幾個步驟的完整執行。如,在擦除DATA分區或CACHE分區過程中,如果發生了重啓、關機等操作,導致沒有擦除成功,那麼再次用常規方式開機後,Bootloader會依據misc分區中BCB的指示,引導進入Recovery,並重新擦除這兩個分區。擦完DATA分區與CACHE分區後,調用finish_recovery,做返回Main system前最後的工作,最終要的是擦除BCB,即MISC分區。此後,用常規方式重新開機,系統會進入Main system。

      閱讀Android的代碼,發現Setting通過RPC調用Checkin Service的masterClear()啓動這個過程,然而在Android中並沒有找到masterClear()的實現,相關代碼需要在產品化的過程中加入。從ICheckinService.aidl的註釋可以瞭解到這個函數的作用:

     /** Reboot into the recovery system and wipe all user data. */

    代碼位置:

     /packages/apps/Settings/src/com/android/settings/MasterClear.java

      /frameworks/base/core/java/android/os/ICheckinService.aidl

 

2. 通過HOME+POWER組合鍵進入Recovery,再按ALT+W啓動Factory data reset

過程比較簡單,而且與上一種方式類似,結合總體流程,步驟如下:

   1) 捕獲按鍵Alt + W。

   2) 擦除DATA分區、擦除CACHE分區。

   3a) 若激活了log顯示(ALT+L:toggle log text display),調用finish_recovery函數重啓,回到Main system。

   3b) 若沒有激活log顯示,繼續接收按鍵,可用HOME+BACK重啓回到Main system。

 六、Android Recovery: Update流程

1. update.zip

    update操作需要升級包,該升級包是文件名是*.zip,但觀察包內結構會發現其實就是JAR包,JAR包是具有特定目錄和文件結構的ZIP壓縮包,因此可以作爲ZIP包解開。

    • MANIFEST.MF:這個manifest文件定義了與包相關數據。

    • XXX.SF:這是JAR文件的簽名文件,佔位符xxx標識簽名者,如CERT。

    • XXX.DSA:與簽名文件相關聯的簽名程序塊文件,它存儲了用於簽名JAR文件的公共簽名。

    • 在META-INF/com/google/android目錄下有updater-script文件,內容就是update要做的操作,也就是前面提到過的command序列。

    出於安全性及版本控制的考慮,JAR包要求必須有完整性以及合法性簽名。可以看出這是Android確保安全的策略。JAR相關內容參見http://www.ibm.com/developerworks/cn/java/j-jar/,這裏就不再詳細介紹。

2. Main system部分
      通過Android系統下載升級包並啓動升級操作,需要上層應用Updater的支持,它是Java程序,代碼位置android/packages/apps/Updater。大致流程:

     • 系統啓動後,如果存在網絡連接,則檢查是否存在升級包;
     • 如果存在升級包,則下載至/cache目錄;
     • 調用Updater程序來提示是否升級;
     • 如果Updater程序進程不存在,則自動啓動此程序;
     • 沒有在代碼中找到開始升級後執行哪些操作。不過由recovery.c的註釋部分可以肯定一定需要重啓進入Recovery,重啓前要更新/cache/recovery/command,以告知Recovery進行升級:
      –update_package=root:path

3. update流程

     update有兩種方式,第一種是上面提到的由Android啓動的自動update過程,升級包在cache/下,升級包的名字在/cache/recovery/command文件中指定。第二種是手動進入Recovery模式,然後輸入Alt + S,安裝/sdcard/update.zip升級包。兩種方式不同的只是安裝包的位置以及傳遞參數給Recovery的方法,update過程都是一樣的,工作流程如下圖所示:

1) install_package @ android/bootable/recovery/install.c
     得到安裝包信息,如“–update_package=CACHE:update.zip”,進入install_package函數,流程如下左圖。mount安裝包所在的分區,然後打開zip壓縮包,進入handle_update_package(Android4.0: try_update_binary,它從zip包中提取並運行update-binary程序,update-binary由/bootable/recovery/updater/updater.c編譯生成,update-binary負責解釋updater-script腳本並執行腳本中的命令)開始升級:

 

4.  Bootloader

      每次啓動,Bootloader都會讀取位於misc分區的bootloader_message,並檢查command區域以\0結尾,還要考慮flash存在壞塊的情況。然後根據讀取的命令,啓動系統或者更新firmware。工作流程如下:

 

     升級之後,無論升級成功是否,Bootloader都會進入recovery完成最後的收尾工作,並帶着status以告知是否成功。如果更新hboot(尚不知道爲什麼叫這個名字,不過可以確定它就是bootloader firmware),一旦失敗,若原有的bootloader遭到破壞,那麼系統將不能boot。 

轉自:http://www.codingguy.net/?p=49

發佈了30 篇原創文章 · 獲贊 4 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章