android R启动找不到super分区问题
總結一個android R打開super動態分區后,init第一階段啟動失敗的例子,也為了自己后面看看趟過的坑。
在移植適配android R項目,主要做了如下事情:
打開BOARD_AVB_ENABLE := true配置 和 添加dynamic動態分區配置
物理分區表添加super分區
檢查過kernel defconfig中的DM相關的配置都已經使能
fstab添加super相關配置的幾個邏輯分區
打開AVB相關配置,關閉dts中的avb相關的分區配置
在以上準備工作完成后,編譯版本,確實super.img鏡像編譯正常,vbmeta和vbmeta_system鏡像也正常,dump顯示內容也正常,燒錄鏡像后開機啟動
果然還是遇到報錯,啟動到init第一階段,報如下錯誤:
遇到這種問題,沒得整,只能硬著頭皮一步步爬了,開扒~~~
從下面這句開始找線索
[ 7.803085] {1}[1:init]init: realpath failed: /dev/block/by-name/super: No such file or directory
擼代碼,找到位置在first_stage_mount.cpp,在init的第一階段初始化分區物理分區節點時報異常了
bool FirstStageMount::InitDevices() {std::set<std::string> devices;GetSuperDeviceName(&devices);if (!GetDmVerityDevices(&devices)) {return false;}if (!InitRequiredDevices(std::move(devices))) {return false;}if (IsDmLinearEnabled()) {auto super_symlink = "/dev/block/by-name/"s + super_partition_name_;if (!android::base::Realpath(super_symlink, &super_path_)) {PLOG(ERROR) << "realpath failed: " << super_symlink;return false;}}return true; }從Realpath函數來看,應該是super_symlink這個節點,在boot啟動后symlink不存在。super物理分區前面我們不是配置了么,且燒錄super分區是正常的,問題出在哪??
在暫時看不明白原因的情況下,添加日志打印是比較好的手段,所以先添加點代碼,判斷一下super分區的節點是否存在,添加的代碼如下,打印一下super_symlink和super_path_分別是什么,以及access判斷一下/dev/block/by-name/super節點是否存在。
if (IsDmLinearEnabled()) {auto super_symlink = "/dev/block/by-name/"s + super_partition_name_;LOG(INFO) << " super_partition_name_ is " << super_partition_name_ << ", super_path_ is " << super_path_;if (access("/dev/block/by-name/super", F_OK) == 0) {LOG(INFO) << "super partition node is existed.";} else {LOG(INFO) << "super partition node is not existed.";}if (!android::base::Realpath(super_symlink, &super_path_)) {PLOG(ERROR) << "realpath failed: " << super_symlink;return false;}}編譯運行后,發現走到else流程,即/dev/block/by-name/super分區節點不存在。好吧,應該不是原生的問題,android base code肯定是好的,估摸著跟其他地方配置有關。還得繼續往下分析。
回想一下android開機的分區掛載流程,腦子里要有一個大概的思路,第一階段要先從ramdisk中讀出fstab分區配置,然后去掛載,
在掛載前要先等底層kernel把分區節點/sys文件系統準備好,不然上層是沒法掛載的,
在init的第一階段表現在這里的InitDevices函數,這個大的方向主要干哪些事呢?
至于怎么知道是_a還是_b,擼過相關代碼的人應該看到過,是在fs_mgr_slotselect.cpp中的fs_mgr_get_slot_suffix函數處理的,這個是從kernel中cmdline中提供的,一般由更底層處理append到cmdline
第一個是先初始化InitDeviceMapper,因為這個比較長,后面我準備在AVB流程中單獨介紹一下,Device mapper的初始化流程要走到kernel MD模塊,我們這里先跳過。
然后調用block_dev_init_.InitDevices去初始化,devices這里是指針,后面走完流程會帶回來值。
調用到了block_dev_initializer.cpp的InitDevices函數,估計不少人看到下面有點蒙,下面這些代碼做了啥了??
@block_dev_initializer.cpp bool BlockDevInitializer::InitDevices(std::set<std::string> devices) {auto uevent_callback = [&, this](const Uevent& uevent) -> ListenerAction {return HandleUevent(uevent, &devices);};uevent_listener_.RegenerateUevents(uevent_callback);// UeventCallback() will remove found partitions from |devices|. So if it// isn't empty here, it means some partitions are not found.if (!devices.empty()) {LOG(INFO) << __PRETTY_FUNCTION__<< ": partition(s) not found in /sys, waiting for their uevent(s): "<< android::base::Join(devices, ", ");Timer t;uevent_listener_.Poll(uevent_callback, 10s);LOG(INFO) << "Wait for partitions returned after " << t;}if (!devices.empty()) {LOG(ERROR) << __PRETTY_FUNCTION__ << ": partition(s) not found after polling timeout: "<< android::base::Join(devices, ", ");return false;}return true; }我來簡單點講下就明白了,要這么想,android開機時kernel創建好文件系統節點,ramdisk中的init進程怎么知道底層有沒有準備,他們怎么通知相互告知狀態?
所以,才采用了epoll機制(如果不明白linux的epoll機制,建議先找一下相關資料瞅瞅)
假如kernel上報的uevent中這幾個分區節點已存在,還要干什么不?當然是創建symlink軟連接,你總不能讓人家去用/dev/block//mmcblk0p24這種物理分區吧?
好了,有了這些概念后,再看上面這段代碼:啟動了一個10秒的定時器,等HandleUevent回調,如果10秒沒有回調成功,直接超時掛掉~
我這里InitDevices已經走完了,并沒有超時,那是哪里的問題?得繼續深追原因~~
從HandleUevent開始分析,添加點打印log
ListenerAction BlockDevInitializer::HandleUevent(const Uevent& uevent,std::set<std::string>* devices) { ...auto iter = devices->find(name);if (iter == devices->end()) {return ListenerAction::kContinue;}LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << name;LOG(ERROR) << "HandleUevent found partition: " << name; //這句是我添加的,用ERROR省事,該有的log都出來了devices->erase(iter);device_handler_->HandleUevent(uevent); //這句是重點,一行代碼決定了好多功能~~return devices->empty() ? ListenerAction::kStop : ListenerAction::kContinue; }運行打印日志如下,確實是我們要的幾個分區,super分區也是在的
[ 7.471662] {1}[1:init]init: HandleUevent found partition: boot_a
[ 7.557741] {1}[1:init]init: HandleUevent found partition: super
[ 7.635216] {1}[1:init]init: HandleUevent found partition: vbmeta_system_a
[ 7.712557] {1}[1:init]init: HandleUevent found partition: vbmeta_a
繼續看device_handler_->HandleUevent(uevent)發生了什么?
device_handle是devices.cpp中DeviceHandler的實例,我們看一下其HandleUevent函數,看完只想說,我是誰我在哪??
抽絲剝繭開始吧,誰叫是碼農呢。
底層上報肯定是block,且有個links,感覺有戲,有點像symlinks的感覺。
links = GetBlockDeviceSymlinks(uevent);
看下GetBlockDeviceSymlinks函數
看到links.emplace_back("/dev/block/by-name/" + uevent.device_name)這句,感覺要接近真實,還記得前面first_stage_mount.cpp中InitDevices報的錯誤不??不記得得往回走走,往上翻翻~~~~
/dev/block/by-name/super這個節點找不到,看到GetBlockDeviceSymlinks函數,應該知道這個節點是拼接起來的,但為什么我們會報錯呢?
先搞點Log再說,且把is_boot_device變量打印一下,下面的邏輯跟這個有點關系。
編譯運行后,內容如下,四次返回的device都是0.soc/fa507000.sdhci,感覺問題可能出在這里,而且is_boot_device都是0
從上面的代碼邏輯來看,貌似需要這個值為true,這樣在mount的時候,才可以找到/dev/block/by-name/super的節點
但為什么原生的代碼,我這里會報錯呢??
而且上面found device怎么是0.soc/fa507000.sdhci,這個又是從哪來的?
之前在看kernel uevent上報消息日志時,貌似看到過,先去源碼目錄下搜索一下0.soc,果然在BoardConfig.mk中找到了cmdline中有定義
androidboot.bootdevices=34458000.sdhci androidboot.boot_devices=0.soc/34458000.sdhci
這個明白原因了吧,bootdevices值和底層上報的不相同,修改一下,應該是不同的項目配置用錯了值。
androidboot.bootdevices=fa507000.sdhci androidboot.boot_devices=0.soc/fa507000.sdhci
把上面bool is_boot_device = true;這句注釋掉,重新編譯和燒錄開機運行,搞定,不再報這個錯誤了。
雖然只是修改了這么一點點,但整個分析的過程挺長的,要對整個android分區掛載流程比較清晰點,以上就是這些分析,mark一下也給其他遇到類似問題的人一個方向。
總結
以上是生活随笔為你收集整理的android R启动找不到super分区问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android底层oem,Android
- 下一篇: HDOJ HDU 2058 The su