Added vendor boot image support for cuttlefish

Bug: 138199351
Bug: 140529116
Test: Treehugger
Change-Id: I38fd5a5faa701634677289a04847243139f4ff2a
diff --git a/host/commands/assemble_cvd/Android.bp b/host/commands/assemble_cvd/Android.bp
index e296526..b03d8bf 100644
--- a/host/commands/assemble_cvd/Android.bp
+++ b/host/commands/assemble_cvd/Android.bp
@@ -39,6 +39,7 @@
         "super_image_mixer.cc",
     ],
     header_libs: [
+        "bootimg_headers",
         "cuttlefish_glog",
     ],
     shared_libs: [
diff --git a/host/commands/assemble_cvd/boot_image_unpacker.cc b/host/commands/assemble_cvd/boot_image_unpacker.cc
index 460cc1e..f1ead30 100644
--- a/host/commands/assemble_cvd/boot_image_unpacker.cc
+++ b/host/commands/assemble_cvd/boot_image_unpacker.cc
@@ -21,10 +21,10 @@
 
 #include <sstream>
 
+#include <bootimg.h>
 #include <glog/logging.h>
 
 #include "common/libs/utils/subprocess.h"
-#include "host/commands/assemble_cvd/bootimg.h"
 
 namespace cvd {
 
@@ -48,39 +48,58 @@
 }
 }  // namespace
 
-std::unique_ptr<BootImageUnpacker> BootImageUnpacker::FromImage(
-    const std::string& path) {
-  auto boot_img = SharedFD::Open(path.c_str(), O_RDONLY);
+std::unique_ptr<BootImageUnpacker> BootImageUnpacker::FromImages(
+    const std::string& boot_image_path,
+    const std::string& vendor_boot_image_path) {
+  auto boot_img = SharedFD::Open(boot_image_path.c_str(), O_RDONLY);
   if (!boot_img->IsOpen()) {
-    LOG(ERROR) << "Unable to open boot image (" << path
+    LOG(ERROR) << "Unable to open boot image (" << boot_image_path
                << "): " << boot_img->StrError();
     return nullptr;
   }
-  boot_img_hdr header;
+  boot_img_hdr_v3 header;
   auto bytes_read = boot_img->Read(&header, sizeof(header));
   if (bytes_read != sizeof(header)) {
     LOG(ERROR) << "Error reading boot image header";
     return nullptr;
   }
 
-  std::ostringstream cmdline;
-  cmdline << reinterpret_cast<char*>(&header.cmdline[0]);
-  if (header.extra_cmdline[0] != '\0') {
-    cmdline << " ";
-    cmdline << reinterpret_cast<char*>(&header.extra_cmdline[0]);
+  auto vendor_boot_img = SharedFD::Open(vendor_boot_image_path.c_str(),
+                                        O_RDONLY);
+  if (!vendor_boot_img->IsOpen()) {
+    LOG(ERROR) << "Unable to open vendor boot image (" << vendor_boot_image_path
+               << "): " << vendor_boot_img->StrError();
+    return nullptr;
+  }
+  vendor_boot_img_hdr_v3 vboot_header;
+  bytes_read = vendor_boot_img->Read(&vboot_header, sizeof(vboot_header));
+  if (bytes_read != sizeof(vboot_header)) {
+    LOG(ERROR) << "Error reading vendor boot image header";
+    return nullptr;
   }
 
-  uint32_t page_size = header.page_size;
-  // See system/core/mkbootimg/include/mkbootimg/bootimg.h for the origin of
+  std::ostringstream cmdline;
+  cmdline << reinterpret_cast<char*>(&header.cmdline[0]);
+  if (vboot_header.cmdline[0] != '\0') {
+    cmdline << " ";
+    cmdline << reinterpret_cast<char*>(&vboot_header.cmdline[0]);
+  }
+
+  uint32_t page_size = 4096;
+  // See system/tools/mkbootimg/include/bootimg/bootimg.h for the origin of
   // these offset calculations
   uint32_t kernel_offset = page_size;
   uint32_t ramdisk_offset =
       kernel_offset +
       ((header.kernel_size + page_size - 1) / page_size) * page_size;
+  uint32_t vendor_ramdisk_offset =
+      ((vboot_header.header_size + vboot_header.page_size - 1) / vboot_header.page_size) *
+      vboot_header.page_size;
 
   std::unique_ptr<BootImageUnpacker> ret(new BootImageUnpacker(
       boot_img, cmdline.str(), header.kernel_size, kernel_offset,
-      header.ramdisk_size, ramdisk_offset));
+      header.ramdisk_size, ramdisk_offset, vendor_boot_img,
+      vboot_header.vendor_ramdisk_size, vendor_ramdisk_offset));
 
   return ret;
 }
@@ -99,8 +118,14 @@
   return ExtractFile(boot_image_, ramdisk_image_offset_, ramdisk_image_size_,
                      path);
 }
+bool BootImageUnpacker::ExtractVendorRamdiskImage(const std::string& path) const {
+  if (vendor_ramdisk_image_size_ == 0) return false;
+  return ExtractFile(vendor_boot_image_, vendor_ramdisk_image_offset_,
+                     vendor_ramdisk_image_size_, path);
+}
 
 bool BootImageUnpacker::Unpack(const std::string& ramdisk_image_path,
+                               const std::string& vendor_ramdisk_image_path,
                                const std::string& kernel_image_path) {
   if (HasRamdiskImage()) {
     if (!ExtractRamdiskImage(ramdisk_image_path)) {
@@ -108,6 +133,12 @@
       return false;
     }
   }
+  if (HasVendorRamdiskImage()) {
+    if (!ExtractVendorRamdiskImage(vendor_ramdisk_image_path)) {
+      LOG(ERROR) << "Error extracting vendor ramdisk from venodr boot image";
+      return false;
+    }
+  }
   if (!kernel_image_path.empty()) {
     if (HasKernelImage()) {
       if (!ExtractKernelImage(kernel_image_path)) {
diff --git a/host/commands/assemble_cvd/boot_image_unpacker.h b/host/commands/assemble_cvd/boot_image_unpacker.h
index 05fe671..a331f0c 100644
--- a/host/commands/assemble_cvd/boot_image_unpacker.h
+++ b/host/commands/assemble_cvd/boot_image_unpacker.h
@@ -29,7 +29,9 @@
  public:
   // Reads header section of boot image at path and returns a BootImageUnpacker
   // preloaded with all the metadata.
-  static std::unique_ptr<BootImageUnpacker> FromImage(const std::string& path);
+  static std::unique_ptr<BootImageUnpacker> FromImages(
+    const std::string& boot_image_path,
+    const std::string& vendor_boot_image_path);
 
   ~BootImageUnpacker() = default;
 
@@ -37,30 +39,32 @@
 
   bool HasKernelImage() const { return kernel_image_size_ > 0; }
   bool HasRamdiskImage() const { return ramdisk_image_size_ > 0; }
-
-  // Extracts the kernel image to the given path
-  bool ExtractKernelImage(const std::string& path) const;
-  // Extracts the ramdisk image to the given path. It may return false if the
-  // boot image does not contain a ramdisk, which is the case when having system
-  // as root.
-  bool ExtractRamdiskImage(const std::string& path) const;
+  bool HasVendorRamdiskImage() const { return vendor_ramdisk_image_size_ > 0; }
 
   bool Unpack(const std::string& ramdisk_image_path,
+              const std::string& vendor_ramdisk_image_path,
               const std::string& kernel_image_path);
 
  private:
   BootImageUnpacker(SharedFD boot_image, const std::string& cmdline,
                     uint32_t kernel_image_size, uint32_t kernel_image_offset,
-                    uint32_t ramdisk_image_size, uint32_t ramdisk_image_offset)
+                    uint32_t ramdisk_image_size, uint32_t ramdisk_image_offset,
+                    SharedFD vendor_boot_image,
+                    uint32_t vendor_ramdisk_image_size,
+                    uint32_t vendor_ramdisk_image_offset)
       : boot_image_(boot_image),
+        vendor_boot_image_(vendor_boot_image),
         kernel_cmdline_(cmdline),
         kernel_image_size_(kernel_image_size),
         kernel_image_offset_(kernel_image_offset),
         ramdisk_image_size_(ramdisk_image_size),
-        ramdisk_image_offset_(ramdisk_image_offset) {}
+        ramdisk_image_offset_(ramdisk_image_offset),
+        vendor_ramdisk_image_size_(vendor_ramdisk_image_size),
+        vendor_ramdisk_image_offset_(vendor_ramdisk_image_offset) {}
 
   // Mutable because we only read from the fd, but do not modify its contents
   mutable SharedFD boot_image_;
+  mutable SharedFD vendor_boot_image_;
   std::string kernel_cmdline_;
   // When buidling the boot image a particular page size is assumed, which may
   // not match the actual page size of the system.
@@ -68,6 +72,17 @@
   uint32_t kernel_image_offset_;
   uint32_t ramdisk_image_size_;
   uint32_t ramdisk_image_offset_;
+  uint32_t vendor_ramdisk_image_size_;
+  uint32_t vendor_ramdisk_image_offset_;
+
+  // Extracts the kernel image to the given path
+  bool ExtractKernelImage(const std::string& path) const;
+  // Extracts the ramdisk image to the given path. It may return false if the
+  // boot image does not contain a ramdisk, which is the case when having system
+  // as root.
+  bool ExtractRamdiskImage(const std::string& path) const;
+  // Extracts the vendor ramdisk image to the given path
+  bool ExtractVendorRamdiskImage(const std::string& path) const;
 };
 
 }  // namespace cvd
diff --git a/host/commands/assemble_cvd/bootimg.h b/host/commands/assemble_cvd/bootimg.h
deleted file mode 100644
index 7b8fb28..0000000
--- a/host/commands/assemble_cvd/bootimg.h
+++ /dev/null
@@ -1,167 +0,0 @@
-/* tools/mkbootimg/bootimg.h
-**
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-// This file is a clone of the one found on system/core/mkbootimg. We've made a
-// clone because the location of the original header has changed over time.
-
-#include <stdint.h>
-
-#ifndef CUTTLEFISH_LAUNCH_BOOT_IMAGE_H_
-#define CUTTLEFISH_LAUNCH_BOOT_IMAGE_H_
-
-#define BOOT_MAGIC "ANDROID!"
-#define BOOT_MAGIC_SIZE 8
-#define BOOT_NAME_SIZE 16
-#define BOOT_ARGS_SIZE 512
-#define BOOT_EXTRA_ARGS_SIZE 1024
-
-#define BOOT_HEADER_VERSION_ZERO 0
-/*
- *  Bootloader expects the structure of boot_img_hdr with header version
- *  BOOT_HEADER_VERSION_ZERO to be as follows:
- */
-struct boot_img_hdr_v0 {
-    uint8_t magic[BOOT_MAGIC_SIZE];
-
-    uint32_t kernel_size; /* size in bytes */
-    uint32_t kernel_addr; /* physical load addr */
-
-    uint32_t ramdisk_size; /* size in bytes */
-    uint32_t ramdisk_addr; /* physical load addr */
-
-    uint32_t second_size; /* size in bytes */
-    uint32_t second_addr; /* physical load addr */
-
-    uint32_t tags_addr; /* physical addr for kernel tags */
-    uint32_t page_size; /* flash page size we assume */
-    /*
-     * version for the boot image header.
-     */
-    uint32_t header_version;
-
-    /* operating system version and security patch level; for
-     * version "A.B.C" and patch level "Y-M-D":
-     * ver = A << 14 | B << 7 | C         (7 bits for each of A, B, C)
-     * lvl = ((Y - 2000) & 127) << 4 | M  (7 bits for Y, 4 bits for M)
-     * os_version = ver << 11 | lvl */
-    uint32_t os_version;
-
-    uint8_t name[BOOT_NAME_SIZE]; /* asciiz product name */
-
-    uint8_t cmdline[BOOT_ARGS_SIZE];
-
-    uint32_t id[8]; /* timestamp / checksum / sha1 / etc */
-
-    /* Supplemental command line data; kept here to maintain
-     * binary compatibility with older versions of mkbootimg */
-    uint8_t extra_cmdline[BOOT_EXTRA_ARGS_SIZE];
-} __attribute__((packed));
-
-/*
- * It is expected that callers would explicitly specify which version of the
- * boot image header they need to use.
- */
-typedef struct boot_img_hdr_v0 boot_img_hdr;
-
-/* When a boot header is of version BOOT_HEADER_VERSION_ZERO, the structure of boot image is as
- * follows:
- *
- * +-----------------+
- * | boot header     | 1 page
- * +-----------------+
- * | kernel          | n pages
- * +-----------------+
- * | ramdisk         | m pages
- * +-----------------+
- * | second stage    | o pages
- * +-----------------+
- *
- * n = (kernel_size + page_size - 1) / page_size
- * m = (ramdisk_size + page_size - 1) / page_size
- * o = (second_size + page_size - 1) / page_size
- *
- * 0. all entities are page_size aligned in flash
- * 1. kernel and ramdisk are required (size != 0)
- * 2. second is optional (second_size == 0 -> no second)
- * 3. load each element (kernel, ramdisk, second) at
- *    the specified physical address (kernel_addr, etc)
- * 4. prepare tags at tag_addr.  kernel_args[] is
- *    appended to the kernel commandline in the tags.
- * 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
- * 6. if second_size != 0: jump to second_addr
- *    else: jump to kernel_addr
- */
-
-#define BOOT_HEADER_VERSION_ONE 1
-
-struct boot_img_hdr_v1 : public boot_img_hdr_v0 {
-    uint32_t recovery_dtbo_size;   /* size in bytes for recovery DTBO image */
-    uint64_t recovery_dtbo_offset; /* physical load addr */
-    uint32_t header_size;
-} __attribute__((packed));
-
-/* When the boot image header has a version of BOOT_HEADER_VERSION_ONE, the structure of the boot
- * image is as follows:
- *
- * +-----------------+
- * | boot header     | 1 page
- * +-----------------+
- * | kernel          | n pages
- * +-----------------+
- * | ramdisk         | m pages
- * +-----------------+
- * | second stage    | o pages
- * +-----------------+
- * | recovery dtbo   | p pages
- * +-----------------+
- * n = (kernel_size + page_size - 1) / page_size
- * m = (ramdisk_size + page_size - 1) / page_size
- * o = (second_size + page_size - 1) / page_size
- * p = (recovery_dtbo_size + page_size - 1) / page_size
- *
- * 0. all entities are page_size aligned in flash
- * 1. kernel and ramdisk are required (size != 0)
- * 2. recovery_dtbo is required for recovery.img in non-A/B devices(recovery_dtbo_size != 0)
- * 3. second is optional (second_size == 0 -> no second)
- * 4. load each element (kernel, ramdisk, second, recovery_dtbo) at
- *    the specified physical address (kernel_addr, etc)
- * 5. prepare tags at tag_addr.  kernel_args[] is
- *    appended to the kernel commandline in the tags.
- * 6. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
- * 7. if second_size != 0: jump to second_addr
- *    else: jump to kernel_addr
- */
-
-#if 0
-typedef struct ptentry ptentry;
-
-struct ptentry {
-    char name[16];      /* asciiz partition name    */
-    unsigned start;     /* starting block number    */
-    unsigned length;    /* length in blocks         */
-    unsigned flags;     /* set to zero              */
-};
-
-/* MSM Partition Table ATAG
-**
-** length: 2 + 7 * n
-** atag:   0x4d534d70
-**         <ptentry> x n
-*/
-#endif
-
-#endif
diff --git a/host/commands/assemble_cvd/flags.cc b/host/commands/assemble_cvd/flags.cc
index 6de4abd..22def1b 100644
--- a/host/commands/assemble_cvd/flags.cc
+++ b/host/commands/assemble_cvd/flags.cc
@@ -71,6 +71,9 @@
 DEFINE_string(boot_image, "",
               "Location of cuttlefish boot image. If empty it is assumed to be "
               "boot.img in the directory specified by -system_image_dir.");
+DEFINE_string(vendor_boot_image, "",
+              "Location of cuttlefish vendor boot image. If empty it is assumed to "
+              "be vendor_boot.img in the directory specified by -system_image_dir.");
 DEFINE_int32(memory_mb, 2048,
              "Total amount of memory available for guest, MB.");
 std::string g_default_mempath{vsoc::GetDefaultMempath()};
@@ -251,6 +254,11 @@
   std::string default_composite_disk = FLAGS_system_image_dir + "/composite.img";
   SetCommandLineOptionWithMode("composite_disk", default_composite_disk.c_str(),
                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  std::string default_vendor_boot_image = FLAGS_system_image_dir
+                                        + "/vendor_boot.img";
+  SetCommandLineOptionWithMode("vendor_boot_image",
+                               default_vendor_boot_image.c_str(),
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
 
   return true;
 }
@@ -325,10 +333,12 @@
   }
 
   auto ramdisk_path = tmp_config_obj.PerInstancePath("ramdisk.img");
+  auto vendor_ramdisk_path = tmp_config_obj.PerInstancePath("vendor_ramdisk.img");
   bool use_ramdisk = boot_image_unpacker.HasRamdiskImage();
   if (!use_ramdisk) {
     LOG(INFO) << "No ramdisk present; assuming system-as-root build";
     ramdisk_path = "";
+    vendor_ramdisk_path = "";
   }
 
   tmp_config_obj.add_kernel_cmdline(boot_image_unpacker.kernel_cmdline());
@@ -405,11 +415,12 @@
   }
 
   tmp_config_obj.set_ramdisk_image_path(ramdisk_path);
+  // Boot as recovery is set so normal boot needs to be forced every boot
+  tmp_config_obj.add_kernel_cmdline("androidboot.force_normal_boot=1");
+  tmp_config_obj.set_vendor_ramdisk_image_path(vendor_ramdisk_path);
+  tmp_config_obj.set_final_ramdisk_path(ramdisk_path + kRamdiskConcatExt);
   if(FLAGS_initramfs_path.size() > 0) {
     tmp_config_obj.set_initramfs_path(FLAGS_initramfs_path);
-    tmp_config_obj.set_final_ramdisk_path(ramdisk_path + kRamdiskConcatExt);
-  } else {
-    tmp_config_obj.set_final_ramdisk_path(ramdisk_path);
   }
 
   tmp_config_obj.set_mempath(FLAGS_mempath);
@@ -791,7 +802,14 @@
     exit(cvd::kCuttlefishConfigurationInitError);
   }
 
-  auto boot_img_unpacker = cvd::BootImageUnpacker::FromImage(FLAGS_boot_image);
+  if (!cvd::FileHasContent(FLAGS_vendor_boot_image)) {
+    LOG(ERROR) << "File not found: " << FLAGS_vendor_boot_image;
+    exit(cvd::kCuttlefishConfigurationInitError);
+  }
+
+  auto boot_img_unpacker =
+    cvd::BootImageUnpacker::FromImages(FLAGS_boot_image,
+                                       FLAGS_vendor_boot_image);
 
   if (!InitializeCuttlefishConfiguration(*boot_img_unpacker)) {
     LOG(ERROR) << "Failed to initialize configuration";
@@ -805,6 +823,7 @@
   }
 
   if (!boot_img_unpacker->Unpack(config->ramdisk_image_path(),
+                                 config->vendor_ramdisk_image_path(),
                                  config->use_unpacked_kernel()
                                      ? config->kernel_image_path()
                                      : "")) {
@@ -812,12 +831,20 @@
     exit(AssemblerExitCodes::kBootImageUnpackError);
   }
 
-  if(config->initramfs_path().size() != 0) {
-    if(!ConcatRamdisks(config->final_ramdisk_path(), config->ramdisk_image_path(),
-        config->initramfs_path())) {
-      LOG(ERROR) << "Failed to concatenate ramdisk and initramfs";
-      exit(AssemblerExitCodes::kInitRamFsConcatError);
-    }
+  // TODO(134522463) as part of the bootloader refactor, repack the vendor boot
+  // image and use the bootloader to load both the boot and vendor ramdisk.
+  // Until then, this hack to get gki modules into cuttlefish will suffice.
+
+  // If a vendor ramdisk comes in via this mechanism, let it supercede the one
+  // in the vendor boot image. This flag is what kernel presubmit testing uses
+  // to pass in the kernel ramdisk.
+  const std::string& vendor_ramdisk_path = config->initramfs_path().size() ?
+                                           config->initramfs_path() :
+                                           config->vendor_ramdisk_image_path();
+  if(!ConcatRamdisks(config->final_ramdisk_path(), config->ramdisk_image_path(),
+                     vendor_ramdisk_path)) {
+    LOG(ERROR) << "Failed to concatenate ramdisk and vendor ramdisk";
+    exit(AssemblerExitCodes::kInitRamFsConcatError);
   }
 
   if (config->decompress_kernel()) {
diff --git a/host/libs/config/cuttlefish_config.cpp b/host/libs/config/cuttlefish_config.cpp
index ade685b..882a951 100644
--- a/host/libs/config/cuttlefish_config.cpp
+++ b/host/libs/config/cuttlefish_config.cpp
@@ -92,6 +92,7 @@
 const char* kRamdiskImagePath = "ramdisk_image_path";
 const char* kInitramfsPath = "initramfs_path";
 const char* kFinalRamdiskPath = "final_ramdisk_path";
+const char* kVendorRamdiskImagePath = "vendor_ramdisk_image_path";
 
 const char* kVirtualDiskPaths = "virtual_disk_paths";
 const char* kUsbV1SocketName = "usb_v1_socket_name";
@@ -380,6 +381,14 @@
   SetPath(kFinalRamdiskPath, final_ramdisk_path);
 }
 
+std::string CuttlefishConfig::vendor_ramdisk_image_path() const {
+  return (*dictionary_)[kVendorRamdiskImagePath].asString();
+}
+void CuttlefishConfig::set_vendor_ramdisk_image_path(
+    const std::string& vendor_ramdisk_image_path) {
+  SetPath(kVendorRamdiskImagePath, vendor_ramdisk_image_path);
+}
+
 std::vector<std::string> CuttlefishConfig::virtual_disk_paths() const {
   std::vector<std::string> virtual_disks;
   auto virtual_disks_json_obj = (*dictionary_)[kVirtualDiskPaths];
diff --git a/host/libs/config/cuttlefish_config.h b/host/libs/config/cuttlefish_config.h
index 09ae4a9..0fca9f0 100644
--- a/host/libs/config/cuttlefish_config.h
+++ b/host/libs/config/cuttlefish_config.h
@@ -156,6 +156,10 @@
   std::string final_ramdisk_path() const;
   void set_final_ramdisk_path(const std::string& final_ramdisk_path);
 
+  std::string vendor_ramdisk_image_path() const;
+  void set_vendor_ramdisk_image_path(const std::string&
+    vendor_ramdisk_image_path);
+
   std::vector<std::string> virtual_disk_paths() const;
   void set_virtual_disk_paths(const std::vector<std::string>& disk_paths);