Launcher unpacks boot image.
The extraction was being performed by crun on glinux and
fetch_artifacts on gce, so it needs to be done in the launcher to stop
depending on crun.
fetch_artifacts should continue to unpack boot images on gce to ensure
the host packages remain capable of booting older builds.
Bug: 110478603
Test: local and gce
Change-Id: I0b925e700ef8b1693f7dc942bf3fa42b9f4daff8
diff --git a/host/commands/launch/Android.bp b/host/commands/launch/Android.bp
index c39aada..9d8a8e4 100644
--- a/host/commands/launch/Android.bp
+++ b/host/commands/launch/Android.bp
@@ -21,6 +21,7 @@
"ril_region_handler.cc",
"vsoc_shared_memory.cc",
"wifi_region_handler.cc",
+ "boot_image_unpacker.cc",
],
header_libs: [
"cuttlefish_glog",
diff --git a/host/commands/launch/boot_image_unpacker.cc b/host/commands/launch/boot_image_unpacker.cc
new file mode 100644
index 0000000..adc6c5b
--- /dev/null
+++ b/host/commands/launch/boot_image_unpacker.cc
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include "host/commands/launch/boot_image_unpacker.h"
+
+#include <string.h>
+#include <unistd.h>
+
+#include <sstream>
+
+#include <glog/logging.h>
+
+#include "common/libs/utils/subprocess.h"
+#include "host/commands/launch/bootimg.h"
+
+namespace cvd {
+
+namespace {
+
+// Extracts size bytes from file, starting at offset bytes from the beginning to
+// path.
+bool ExtractFile(SharedFD source, off_t offset, size_t size,
+ const std::string& path) {
+ auto dest = SharedFD::Open(path.c_str(), O_CREAT | O_RDWR, 0755);
+ if (!dest->IsOpen()) {
+ LOG(ERROR) << "Unable to open " << path;
+ return false;
+ }
+ auto off = source->LSeek(offset, SEEK_SET);
+ if (off != offset) {
+ LOG(ERROR) << "Failed to lseek: " << source->StrError();
+ return false;
+ }
+ return dest->CopyFrom(*source, size);
+}
+} // namespace
+
+std::unique_ptr<BootImageUnpacker> BootImageUnpacker::FromImage(
+ const std::string& path) {
+ auto boot_img = SharedFD::Open(path.c_str(), O_RDONLY);
+ if (!boot_img->IsOpen()) {
+ LOG(ERROR) << "Unable to open boot image (" << path
+ << "): " << boot_img->StrError();
+ return nullptr;
+ }
+ boot_img_hdr 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]);
+ }
+
+ uint32_t page_size = header.page_size;
+ // See system/core/mkbootimg/include/mkbootimg/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;
+
+ std::unique_ptr<BootImageUnpacker> ret(new BootImageUnpacker(
+ boot_img, cmdline.str(), header.kernel_size, kernel_offset,
+ header.ramdisk_size, ramdisk_offset));
+
+ return ret;
+}
+
+std::string BootImageUnpacker::kernel_command_line() const {
+ return kernel_command_line_;
+}
+
+bool BootImageUnpacker::ExtractKernelImage(const std::string& path) const {
+ if (kernel_image_size_ == 0) return false;
+ return ExtractFile(boot_image_, kernel_image_offset_, kernel_image_size_,
+ path);
+}
+bool BootImageUnpacker::ExtractRamdiskImage(const std::string& path) const {
+ if (ramdisk_image_size_ == 0) return false;
+ return ExtractFile(boot_image_, ramdisk_image_offset_, ramdisk_image_size_,
+ path);
+}
+
+} // namespace cvd
diff --git a/host/commands/launch/boot_image_unpacker.h b/host/commands/launch/boot_image_unpacker.h
new file mode 100644
index 0000000..856c1bc
--- /dev/null
+++ b/host/commands/launch/boot_image_unpacker.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+#pragma once
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include "common/libs/fs/shared_fd.h"
+
+namespace cvd {
+
+// Unpacks the boot image and extracts kernel, ramdisk and kernel arguments
+class BootImageUnpacker {
+ 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);
+
+ ~BootImageUnpacker() = default;
+
+ std::string kernel_command_line() const;
+
+ 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;
+
+ 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)
+ : boot_image_(boot_image),
+ kernel_command_line_(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) {}
+
+ // Mutable because we only read from the fd, but do not modify its contents
+ mutable SharedFD boot_image_;
+ std::string kernel_command_line_;
+ // When buidling the boot image a particular page size is assumed, which may
+ // not match the actual page size of the system.
+ uint32_t kernel_image_size_;
+ uint32_t kernel_image_offset_;
+ uint32_t ramdisk_image_size_;
+ uint32_t ramdisk_image_offset_;
+};
+
+} // namespace cvd
diff --git a/host/commands/launch/bootimg.h b/host/commands/launch/bootimg.h
new file mode 100644
index 0000000..7b8fb28
--- /dev/null
+++ b/host/commands/launch/bootimg.h
@@ -0,0 +1,167 @@
+/* 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/launch/main.cc b/host/commands/launch/main.cc
index b41de1f..30b890d 100644
--- a/host/commands/launch/main.cc
+++ b/host/commands/launch/main.cc
@@ -42,6 +42,7 @@
#include "common/libs/utils/size_utils.h"
#include "common/vsoc/lib/vsoc_memory.h"
#include "common/vsoc/shm/screen_layout.h"
+#include "host/commands/launch/boot_image_unpacker.h"
#include "host/commands/launch/pre_launch_initializers.h"
#include "host/commands/launch/vsoc_shared_memory.h"
#include "host/libs/config/cuttlefish_config.h"
@@ -78,10 +79,7 @@
"Disable DAC security in libvirt. For debug only.");
DEFINE_string(extra_kernel_command_line, "",
"Additional flags to put on the kernel command line");
-DEFINE_string(initrd, "", "Location of cuttlefish initrd file.");
-DEFINE_string(kernel, "", "Location of cuttlefish kernel file.");
-DEFINE_string(kernel_command_line, "",
- "Location of a text file with the kernel command line.");
+DEFINE_string(boot_image, "", "Location of cuttlefish boot image.");
DEFINE_int32(memory_mb, 2048,
"Total amount of memory available for guest, MB.");
std::string g_default_mempath{GetPerInstanceDefault("/var/run/shm/cvd-")};
@@ -432,27 +430,11 @@
// If user did not specify location of either of these files, expect them to
// be placed in --system_image_dir location.
- if (FLAGS_kernel.empty()) {
- FLAGS_kernel = FLAGS_system_image_dir + "/kernel";
- }
- if (FLAGS_kernel_command_line.empty()) {
- FLAGS_kernel_command_line = FLAGS_system_image_dir + "/cmdline";
- }
if (FLAGS_system_image.empty()) {
FLAGS_system_image = FLAGS_system_image_dir + "/system.img";
}
- if (FLAGS_initrd.empty()) {
- FLAGS_initrd = FLAGS_system_image_dir + "/ramdisk.img";
- }
- if (!FileHasContent(FLAGS_initrd.c_str())) {
- FLAGS_initrd.clear();
- }
- if (FLAGS_dtb.empty()) {
- if (FLAGS_initrd.empty()) {
- FLAGS_dtb = vsoc::DefaultHostArtifactsPath("config/system-root.dtb");
- } else {
- FLAGS_dtb = vsoc::DefaultHostArtifactsPath("config/initrd-root.dtb");
- }
+ if (FLAGS_boot_image.empty()) {
+ FLAGS_boot_image = FLAGS_system_image_dir + "/boot.img";
}
if (FLAGS_cache_image.empty()) {
FLAGS_cache_image = FLAGS_system_image_dir + "/cache.img";
@@ -471,8 +453,8 @@
// Check that the files exist
for (const auto& file :
- {FLAGS_system_image, FLAGS_vendor_image, FLAGS_cache_image, FLAGS_kernel,
- FLAGS_data_image, FLAGS_kernel_command_line}) {
+ {FLAGS_system_image, FLAGS_vendor_image, FLAGS_cache_image,
+ FLAGS_data_image, FLAGS_boot_image}) {
if (!FileHasContent(file.c_str())) {
LOG(FATAL) << "File not found: " << file;
return false;
@@ -481,10 +463,28 @@
return true;
}
-bool SetUpGlobalConfiguration() {
- if (!ResolveInstanceFiles()) {
+bool UnpackBootImage(const cvd::BootImageUnpacker& boot_image_unpacker) {
+ auto config = vsoc::CuttlefishConfig::Get();
+ if (boot_image_unpacker.HasRamdiskImage()) {
+ if (!boot_image_unpacker.ExtractRamdiskImage(config->ramdisk_image_path())) {
+ LOG(FATAL) << "Error extracting ramdisk from boot image";
+ return false;
+ }
+ }
+ if (boot_image_unpacker.HasKernelImage()) {
+ if (!boot_image_unpacker.ExtractKernelImage(config->kernel_image_path())) {
+ LOG(FATAL) << "Error extracting kernel from boot image";
+ return false;
+ }
+ } else {
+ LOG(FATAL) << "No kernel found on boot image";
return false;
}
+ return true;
+}
+
+bool SetUpGlobalConfiguration(
+ const cvd::BootImageUnpacker& boot_image_unpacker) {
auto& memory_layout = *vsoc::VSoCMemoryLayout::Get();
auto config = vsoc::CuttlefishConfig::Get();
// Set this first so that calls to PerInstancePath below are correct
@@ -503,20 +503,38 @@
config->set_y_res(FLAGS_y_res);
config->set_refresh_rate_hz(FLAGS_refresh_rate_hz);
- config->set_kernel_image_path(FLAGS_kernel);
- std::ostringstream extra_cmdline;
- if (FLAGS_initrd.empty()) {
- extra_cmdline << " root=/dev/vda init=/init";
- }
- extra_cmdline << " androidboot.serialno=" << FLAGS_serial_number;
- extra_cmdline << " androidboot.lcd_density=" << FLAGS_dpi;
- if (FLAGS_extra_kernel_command_line.size()) {
- extra_cmdline << " " << FLAGS_extra_kernel_command_line;
- }
- config->ReadKernelArgs(FLAGS_kernel_command_line.c_str(),
- extra_cmdline.str());
+ config->set_kernel_image_path(config->PerInstancePath("kernel"));
- config->set_ramdisk_image_path(FLAGS_initrd);
+ auto ramdisk_path = config->PerInstancePath("ramdisk.img");
+ bool use_ramdisk = boot_image_unpacker.HasRamdiskImage();
+ if (!use_ramdisk) {
+ LOG(INFO) << "No ramdisk present; assuming system-as-root build";
+ ramdisk_path = "";
+ }
+
+ // This needs to be done here because the dtb path depends on the presence of
+ // the ramdisk
+ if (FLAGS_dtb.empty()) {
+ if (use_ramdisk) {
+ FLAGS_dtb = vsoc::DefaultHostArtifactsPath("config/initrd-root.dtb");
+ } else {
+ FLAGS_dtb = vsoc::DefaultHostArtifactsPath("config/system-root.dtb");
+ }
+ }
+
+ std::ostringstream kernel_cmdline;
+ kernel_cmdline << boot_image_unpacker.kernel_command_line();
+ if (!use_ramdisk) {
+ kernel_cmdline << " root=/dev/vda init=/init";
+ }
+ kernel_cmdline << " androidboot.serialno=" << FLAGS_serial_number;
+ kernel_cmdline << " androidboot.lcd_density=" << FLAGS_dpi;
+ if (FLAGS_extra_kernel_command_line.size()) {
+ kernel_cmdline << " " << FLAGS_extra_kernel_command_line;
+ }
+ config->set_kernel_args(kernel_cmdline.str());
+
+ config->set_ramdisk_image_path(ramdisk_path);
config->set_system_image_path(FLAGS_system_image);
config->set_cache_image_path(FLAGS_cache_image);
config->set_data_image_path(FLAGS_data_image);
@@ -626,8 +644,12 @@
::android::base::InitLogging(argv, android::base::StderrLogger);
ParseCommandLineFlags(argc, argv);
+ if (!ResolveInstanceFiles()) {
+ return -1;
+ }
+ auto boot_img_unpacker = cvd::BootImageUnpacker::FromImage(FLAGS_boot_image);
// Do this early so that the config object is ready for anything that needs it
- if (!SetUpGlobalConfiguration()) {
+ if (!SetUpGlobalConfiguration(*boot_img_unpacker)) {
return -1;
}
@@ -635,6 +657,11 @@
LOG(FATAL) << "Failed to clean prior files";
}
+ if (!UnpackBootImage(*boot_img_unpacker)) {
+ LOG(ERROR) << "Failed to unpack boot image";
+ return -1;
+ }
+
if (!WriteCuttlefishEnvironment()) {
LOG(ERROR) << "Unable to write cuttlefish environment file";
}
diff --git a/host/libs/config/cuttlefish_config.cpp b/host/libs/config/cuttlefish_config.cpp
index 2ef2058..18a17f3 100644
--- a/host/libs/config/cuttlefish_config.cpp
+++ b/host/libs/config/cuttlefish_config.cpp
@@ -414,24 +414,6 @@
return GetPerInstanceDefault("cvd-");
}
-bool CuttlefishConfig::ReadKernelArgs(const std::string& cmdline_file,
- const std::string& extra_args) {
- std::ostringstream kernel_args;
- std::ifstream cmd_stream(cmdline_file);
- if (!cmd_stream) {
- LOG(WARNING) << "Unable to open " << cmdline_file;
- return false;
- } else {
- kernel_args << cmd_stream.rdbuf();
- cmd_stream.close();
- }
- if (!extra_args.empty()) {
- kernel_args << " " << extra_args;
- }
- set_kernel_args(kernel_args.str());
- return true;
-}
-
int GetInstance() {
static int instance_id = InstanceFromEnvironment();
return instance_id;
diff --git a/host/libs/config/cuttlefish_config.h b/host/libs/config/cuttlefish_config.h
index 3eb5532..e5f7817 100644
--- a/host/libs/config/cuttlefish_config.h
+++ b/host/libs/config/cuttlefish_config.h
@@ -44,10 +44,6 @@
set_usb_v1_socket_name("");
}
- // Reads the kernel command line from a file and appends extra arguments.
- bool ReadKernelArgs(const std::string& cmdline_file,
- const std::string& extra_args);
-
std::string instance_dir() const;
void set_instance_dir(const std::string& instance_dir);