kernel cmdline 转换 properity过程

kernel cmdline 转换 properity过程

        之前遇到一个有关“androidboot.slot_suffix”的问题,发现该kernel cmdline作为后缀会被用来拼接位于fastab中的分区名(如vendor、oem分区等)。如果该cmdline的值传递不正确会使fstab中的分区挂载点出错,导致无法正确挂载分区。通过阅读源码定位到更新fstab中分区后缀的代码位于/system/core/fs_mgr/fs_mgr_boot_config.cpp中,其代码实现为:
40 // Updates |fstab| for slot_suffix. Returns true on success, false on error.
41 bool fs_mgr_update_for_slotselect(struct fstab *fstab) {
42     int n;
43     std::string ab_suffix;
44
45     for (n = 0; n < fstab->num_entries; n++) {
46         if (fstab->recs[n].fs_mgr_flags & MF_SLOTSELECT) {
47             char *tmp;
48             if (ab_suffix.empty()) {
49                 ab_suffix = fs_mgr_get_slot_suffix();
50                 // Returns false as non A/B devices should not have MF_SLOTSELECT.
51                 if (ab_suffix.empty()) return false;
52             }
53             if (asprintf(&tmp, "%s%s", fstab->recs[n].blk_device, ab_suffix.c_str()) > 0) {
54                 free(fstab->recs[n].blk_device);
55                 fstab->recs[n].blk_device = tmp;
56             } else {
57                 return false;
58             }
59         }
60     }
61     return true;
        上述代码首先通过fatab中的参数判断分区是否需要进行分区选择,之后通过fs_mgr_get_slot_suffix()获取当前所启动的slot后缀并完成拼接。fs_mgr_get_slot_suffix()中主要调用了fs_mgr_get_boot_config()来完成分区后缀的获取功能,其中fs_mgr_get_boot_config()的代码实现为:
24 // Tries to get the boot config value in properties, kernel cmdline and
25 // device tree (in that order).  returns 'true' if successfully found, 'false'
26 // otherwise
27 bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val) {
28     FS_MGR_CHECK(out_val != nullptr);
29
30     // first check if we have "ro.boot" property already
31     *out_val = android::base::GetProperty("ro.boot." + key, "");
32     if (!out_val->empty()) {
33         return true;
34  
35     }
36
37     // fallback to kernel cmdline, properties may not be ready yet
38     std::string cmdline;
39     std::string cmdline_key("androidboot." + key);
40     if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
41         for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
42             std::vector<std::string> pieces = android::base::Split(entry, "=");
43             if (pieces.size() == 2) {
44                 if (pieces[0] == cmdline_key) {
45                     *out_val = pieces[1];
46                     return true;
47                 }
48             }
49         }
50     }
51
52     // lastly, check the device tree
53     if (is_dt_compatible()) {
54         std::string file_name = kAndroidDtDir + "/" + key;
55         // DT entries terminate with ' ' but so do the properties
56         if (android::base::ReadFileToString(file_name, out_val)) {
57             return true;
58         }
59
60         LINFO << "Error finding '" << key << "' in device tree";
61     }
62
63     return false;
        获取分区后缀的先后次序为:property > kernel cmdline > dt。通过测试发现系统一般是获取ro.slot_suffix这个property值,并且ro.slot_suffix这个property也是通过androidboot.slot_suffix所转换得到的。继续阅读源码,定位到在init进程中有一处调用process_kernel_cmdline(),继续追踪发现其内部调用了import_kernel_cmdline()并传入了import_kernel_nv()回调函数。其中import_kernel_cmdline()的实现为:
275 void import_kernel_cmdline(bool in_qemu,
276                            const std::function<void(const std::string&, const std::string&, bool)>& fn) {
277     std::string cmdline;
278     android::base::ReadFileToString("/proc/cmdline", &cmdline);
279
280     for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
281         std::vector<std::string> pieces = android::base::Split(entry, "=");
282         if (pieces.size() == 2) {
283             fn(pieces[0], pieces[1], in_qemu);
284         }
285     }
286 }
        上述代码的功能是将传入的cmdline进行分割,并将其传入import_kernel_nv()中进行转换。import_kernel_nv()的代码实现为:
440 static void import_kernel_nv(const std::string& key, const std::string& value, bool for_emulator) {
441     if (key.empty()) return;                                                                       
442                                                                                                    
443     if (for_emulator) {                                                                            
444         // In the emulator, export any kernel option with the "ro.kernel." prefix.                 
445         property_set(StringPrintf("ro.kernel.%s", key.c_str()).c_str(), value.c_str());            
446         return;                                                                                    
447     }                                                                                              
448                                                                                                    
449     if (key == "qemu") {                                                                           
450         strlcpy(qemu, value.c_str(), sizeof(qemu));                                                
451     } else if (android::base::StartsWith(key, "androidboot.")) {                                   
452         property_set(StringPrintf("ro.boot.%s", key.c_str() + 12).c_str(), value.c_str());         
453     }                                                                                              
454 } 
      import_kernel_nv()的功能就是将kernel cmdline中的“androidboot.*”参数转化为"ro.boot.*"property。