diff --git a/base/cvd/cuttlefish/host/commands/assemble_cvd/BUILD.bazel b/base/cvd/cuttlefish/host/commands/assemble_cvd/BUILD.bazel index 604f3e9e28..545285795e 100644 --- a/base/cvd/cuttlefish/host/commands/assemble_cvd/BUILD.bazel +++ b/base/cvd/cuttlefish/host/commands/assemble_cvd/BUILD.bazel @@ -109,6 +109,7 @@ cf_cc_library( "//cuttlefish/common/libs/utils:files", "//cuttlefish/common/libs/utils:subprocess", "//cuttlefish/common/libs/utils:subprocess_managed_stdio", + "//cuttlefish/host/commands/assemble_cvd/boot_image", "//cuttlefish/host/commands/assemble_cvd/disk:generate_persistent_bootconfig", "//cuttlefish/host/libs/avb", "//cuttlefish/host/libs/config:config_utils", diff --git a/base/cvd/cuttlefish/host/commands/assemble_cvd/boot_image/BUILD.bazel b/base/cvd/cuttlefish/host/commands/assemble_cvd/boot_image/BUILD.bazel new file mode 100644 index 0000000000..7923b893eb --- /dev/null +++ b/base/cvd/cuttlefish/host/commands/assemble_cvd/boot_image/BUILD.bazel @@ -0,0 +1,17 @@ +load("//cuttlefish/bazel:rules.bzl", "cf_cc_library") + +package( + default_visibility = ["//:android_cuttlefish"], +) + +cf_cc_library( + name = "boot_image", + srcs = ["boot_image.cc"], + hdrs = ["boot_image.h"], + deps = [ + "//cuttlefish/common/libs/fs", + "//cuttlefish/result", + "@abseil-cpp//absl/strings", + "@mkbootimg//:bootimg_header", + ], +) diff --git a/base/cvd/cuttlefish/host/commands/assemble_cvd/boot_image/boot_image.cc b/base/cvd/cuttlefish/host/commands/assemble_cvd/boot_image/boot_image.cc new file mode 100644 index 0000000000..2b9be11545 --- /dev/null +++ b/base/cvd/cuttlefish/host/commands/assemble_cvd/boot_image/boot_image.cc @@ -0,0 +1,99 @@ +// +// Copyright (C) 2026 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 "cuttlefish/host/commands/assemble_cvd/boot_image/boot_image.h" + +#include + +#include +#include +#include + +#include "absl/strings/str_cat.h" +#include "bootimg.h" + +#include "cuttlefish/common/libs/fs/shared_buf.h" +#include "cuttlefish/common/libs/fs/shared_fd.h" +#include "cuttlefish/result/result.h" + +namespace cuttlefish { + +// https://source.android.com/docs/core/architecture/bootloader/boot-image-header + +Result BootImage::Read(std::string_view path) { + const SharedFD fd = SharedFD::Open(std::string(path), O_RDONLY); + CF_EXPECTF(fd->IsOpen(), "Failed to open '{}': '{}'", path, fd->StrError()); + + std::string magic(BOOT_MAGIC_SIZE, ' '); + CF_EXPECT_EQ(ReadExact(fd, &magic), BOOT_MAGIC_SIZE, fd->StrError()); + CF_EXPECT_EQ(magic, BOOT_MAGIC); + + const off_t version_off = BOOT_MAGIC_SIZE + (sizeof(uint32_t) * 8); + CF_EXPECT_EQ(fd->LSeek(version_off, SEEK_SET), version_off, fd->StrError()); + uint32_t version; + CF_EXPECT_EQ(ReadExactBinary(fd, &version), sizeof(version), fd->StrError()); + + CF_EXPECT_EQ(fd->LSeek(SEEK_SET, 0), 0, fd->StrError()); + + switch (version) { + case 0: { + boot_img_hdr_v0 v0; + CF_EXPECT_EQ(ReadExactBinary(fd, &v0), sizeof(v0), fd->StrError()); + return BootImage(fd, v0); + } + case 1: { + boot_img_hdr_v1 v1; + CF_EXPECT_EQ(ReadExactBinary(fd, &v1), sizeof(v1), fd->StrError()); + return BootImage(fd, v1); + } + case 2: { + boot_img_hdr_v2 v2; + CF_EXPECT_EQ(ReadExactBinary(fd, &v2), sizeof(v2), fd->StrError()); + return BootImage(fd, v2); + } + case 3: { + boot_img_hdr_v3 v3; + CF_EXPECT_EQ(ReadExactBinary(fd, &v3), sizeof(v3), fd->StrError()); + return BootImage(fd, v3); + } + case 4: { + boot_img_hdr_v4 v4; + CF_EXPECT_EQ(ReadExactBinary(fd, &v4), sizeof(v4), fd->StrError()); + return BootImage(fd, v4); + } + default: + return CF_ERRF("Unknown header version {}", version); + } +} + +BootImage::BootImage(SharedFD fd, HeaderVariant header) + : fd_(fd), header_(header) {} + +static std::string KernelCommandLineImpl(const boot_img_hdr_v0& v0) { + const char* cmdline = reinterpret_cast(v0.cmdline); + const char* extra_cmdline(reinterpret_cast(v0.extra_cmdline)); + return absl::StrCat(cmdline, extra_cmdline); +} + +static std::string KernelCommandLineImpl(const boot_img_hdr_v3& v3) { + return std::string(reinterpret_cast(v3.cmdline)); +} + +std::string BootImage::KernelCommandLine() const { + auto visitor = [](const auto& hdr) { return KernelCommandLineImpl(hdr); }; + return std::visit(visitor, header_); +} + +} // namespace cuttlefish diff --git a/base/cvd/cuttlefish/host/commands/assemble_cvd/boot_image/boot_image.h b/base/cvd/cuttlefish/host/commands/assemble_cvd/boot_image/boot_image.h new file mode 100644 index 0000000000..ad8159f4c4 --- /dev/null +++ b/base/cvd/cuttlefish/host/commands/assemble_cvd/boot_image/boot_image.h @@ -0,0 +1,45 @@ +// +// Copyright (C) 2026 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 +#include + +#include "bootimg.h" + +#include "cuttlefish/common/libs/fs/shared_fd.h" +#include "cuttlefish/result/result.h" + +namespace cuttlefish { + +class BootImage { + public: + static Result Read(std::string_view path); + + std::string KernelCommandLine() const; + + private: + using HeaderVariant = + std::variant; + + BootImage(SharedFD, HeaderVariant); + + SharedFD fd_; + HeaderVariant header_; +}; + +} // namespace cuttlefish diff --git a/base/cvd/cuttlefish/host/commands/assemble_cvd/boot_image_utils.cc b/base/cvd/cuttlefish/host/commands/assemble_cvd/boot_image_utils.cc index 223ee6a2d7..32686722ed 100644 --- a/base/cvd/cuttlefish/host/commands/assemble_cvd/boot_image_utils.cc +++ b/base/cvd/cuttlefish/host/commands/assemble_cvd/boot_image_utils.cc @@ -33,6 +33,7 @@ #include "cuttlefish/common/libs/utils/files.h" #include "cuttlefish/common/libs/utils/subprocess.h" #include "cuttlefish/common/libs/utils/subprocess_managed_stdio.h" +#include "cuttlefish/host/commands/assemble_cvd/boot_image/boot_image.h" #include "cuttlefish/host/libs/avb/avb.h" #include "cuttlefish/host/libs/config/config_utils.h" #include "cuttlefish/host/libs/config/known_paths.h" @@ -275,10 +276,10 @@ Result RepackBootImage(const Avb& avb, const std::string& boot_image_path, const std::string& new_boot_image_path, const std::string& build_dir) { - std::string boot_params = - CF_EXPECT(UnpackBootImage(boot_image_path, build_dir)); + CF_EXPECT(UnpackBootImage(boot_image_path, build_dir)); - auto kernel_cmdline = ExtractValue(boot_params, "command line args: "); + BootImage boot_image = CF_EXPECT(BootImage::Read(boot_image_path)); + std::string kernel_cmdline = boot_image.KernelCommandLine(); VLOG(0) << "Cmdline from boot image is " << kernel_cmdline; auto tmp_boot_image_path = new_boot_image_path + TMP_EXTENSION;