init: handle more bootconfig parameters

As parameters are moved from kernel cmdline to bootconfig,
first_stage_init needs to be updated to handle the new
location.
/proc/bootconfig should be checked first, if not present, then check
/proc/cmdline.

Test: launch_cvd
Test: launch_cvd with 4.19 kernel artifacts that do not support
bootconfig
Test: Both of the above configurations with --num_instances 0 or 4
Test: Both configurations with androidboot.boot_devices or
androidboot.boot_device set
Bug: 173815685

Change-Id: I03743f922351d58375e8b9a903899b8bc54bd71e
diff --git a/init/first_stage_console.cpp b/init/first_stage_console.cpp
index 0f01166..e2ea0ab 100644
--- a/init/first_stage_console.cpp
+++ b/init/first_stage_console.cpp
@@ -105,8 +105,20 @@
     _exit(127);
 }
 
-int FirstStageConsole(const std::string& cmdline) {
-    auto pos = cmdline.find("androidboot.first_stage_console=");
+int FirstStageConsole(const std::string& cmdline, const std::string& bootconfig) {
+    auto pos = bootconfig.find("androidboot.first_stage_console =");
+    if (pos != std::string::npos) {
+        int val = 0;
+        if (sscanf(bootconfig.c_str() + pos, "androidboot.first_stage_console = \"%d\"", &val) !=
+            1) {
+            return FirstStageConsoleParam::DISABLED;
+        }
+        if (val <= FirstStageConsoleParam::MAX_PARAM_VALUE && val >= 0) {
+            return val;
+        }
+    }
+
+    pos = cmdline.find("androidboot.first_stage_console=");
     if (pos != std::string::npos) {
         int val = 0;
         if (sscanf(cmdline.c_str() + pos, "androidboot.first_stage_console=%d", &val) != 1) {
diff --git a/init/first_stage_console.h b/init/first_stage_console.h
index d5744df..4a30d35 100644
--- a/init/first_stage_console.h
+++ b/init/first_stage_console.h
@@ -29,7 +29,7 @@
 };
 
 void StartConsole(const std::string& cmdline);
-int FirstStageConsole(const std::string& cmdline);
+int FirstStageConsole(const std::string& cmdline, const std::string& bootconfig);
 
 }  // namespace init
 }  // namespace android
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index ff75aa3..b2ab550 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -102,8 +102,9 @@
     }
 }
 
-bool ForceNormalBoot(const std::string& cmdline) {
-    return cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
+bool ForceNormalBoot(const std::string& cmdline, const std::string& bootconfig) {
+    return bootconfig.find("androidboot.force_normal_boot = \"1\"") != std::string::npos ||
+           cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
 }
 
 }  // namespace
@@ -211,6 +212,8 @@
     android::base::ReadFileToString("/proc/cmdline", &cmdline);
     // Don't expose the raw bootconfig to unprivileged processes.
     chmod("/proc/bootconfig", 0440);
+    std::string bootconfig;
+    android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
     gid_t groups[] = {AID_READPROC};
     CHECKCALL(setgroups(arraysize(groups), groups));
     CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
@@ -278,11 +281,11 @@
         old_root_dir.reset();
     }
 
-    auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline) : 0;
+    auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline, bootconfig) : 0;
 
     boot_clock::time_point module_start_time = boot_clock::now();
     int module_count = 0;
-    if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline), want_console,
+    if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig), want_console,
                            module_count)) {
         if (want_console != FirstStageConsoleParam::DISABLED) {
             LOG(ERROR) << "Failed to load kernel modules, starting console";
@@ -324,7 +327,7 @@
         LOG(INFO) << "Copied ramdisk prop to " << dest;
     }
 
-    if (ForceNormalBoot(cmdline)) {
+    if (ForceNormalBoot(cmdline, bootconfig)) {
         mkdir("/first_stage_ramdisk", 0755);
         // SwitchRoot() must be called with a mount point as the target, so we bind mount the
         // target directory to itself here.