diff --git a/fastboot/Android.bp b/fastboot/Android.bp index bfe0768f8..b61fbd4df 100644 --- a/fastboot/Android.bp +++ b/fastboot/Android.bp @@ -430,6 +430,7 @@ cc_test_host { ], data: [ ":fastboot_test_dtb", + ":fastboot_test_dtb_replace", ":fastboot_test_bootconfig", ":fastboot_test_vendor_ramdisk_none", ":fastboot_test_vendor_ramdisk_platform", diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp index 6b9e493eb..156dc3b33 100644 --- a/fastboot/fastboot.cpp +++ b/fastboot/fastboot.cpp @@ -552,6 +552,12 @@ static int show_help() { " Secondary images may be flashed to inactive slot.\n" " flash PARTITION [FILENAME] Flash given partition, using the image from\n" " $ANDROID_PRODUCT_OUT if no filename is given.\n" + " flash vendor_boot:RAMDISK [FILENAME]\n" + " Flash vendor_boot ramdisk, fetching the existing\n" + " vendor_boot image and repackaging it with the new\n" + " ramdisk.\n" + " --dtb DTB If set with flash vendor_boot:RAMDISK, then\n" + " update the vendor_boot image with provided DTB.\n" "\n" "basics:\n" " devices [-l] List devices in bootloader (-l: with device paths).\n" @@ -1020,6 +1026,8 @@ static uint64_t get_uint_var(const char* var_name, fastboot::IFastBootDriver* fb } int64_t get_sparse_limit(int64_t size, const FlashingPlan* fp) { + if (!fp) return 0; + int64_t limit = int64_t(fp->sparse_limit); if (limit == 0) { // Unlimited, so see what the target device's limit is. @@ -1465,6 +1473,7 @@ static void do_fetch(const std::string& partition, const std::string& slot_overr static std::string repack_ramdisk(const char* pname, struct fastboot_buffer* buf, fastboot::IFastBootDriver* fb) { std::string_view pname_sv{pname}; + struct fastboot_buffer dtb_buf = {.sz = 0, .fd = unique_fd(-1)}; if (!android::base::StartsWith(pname_sv, "vendor_boot:") && !android::base::StartsWith(pname_sv, "vendor_boot_a:") && @@ -1480,10 +1489,25 @@ static std::string repack_ramdisk(const char* pname, struct fastboot_buffer* buf std::string partition(pname_sv.substr(0, pname_sv.find(':'))); std::string ramdisk(pname_sv.substr(pname_sv.find(':') + 1)); + if (!g_dtb_path.empty()) { + if (!load_buf(g_dtb_path.c_str(), &dtb_buf, nullptr)) { + die("cannot load '%s': %s", g_dtb_path.c_str(), strerror(errno)); + } + + if (dtb_buf.type != FB_BUFFER_FD) { + die("Flashing sparse vendor ramdisk image with dtb is not supported."); + } + if (dtb_buf.sz <= 0) { + die("repack_ramdisk() sees invalid dtb size: %" PRId64, buf->sz); + } + verbose("Updating DTB with %s", pname_sv.data()); + } + unique_fd vendor_boot(make_temporary_fd("vendor boot repack")); uint64_t vendor_boot_size = fetch_partition(partition, vendor_boot, fb); auto repack_res = replace_vendor_ramdisk(vendor_boot, vendor_boot_size, ramdisk, buf->fd, - static_cast(buf->sz)); + static_cast(buf->sz), dtb_buf.fd, + static_cast(dtb_buf.sz)); if (!repack_res.ok()) { die("%s", repack_res.error().message().c_str()); } diff --git a/fastboot/fuzzer/fastboot_fuzzer.cpp b/fastboot/fuzzer/fastboot_fuzzer.cpp index 60940fe3d..4594a8ab4 100644 --- a/fastboot/fuzzer/fastboot_fuzzer.cpp +++ b/fastboot/fuzzer/fastboot_fuzzer.cpp @@ -15,6 +15,7 @@ * */ #include +#include #include "fastboot.h" #include "socket.h" #include "socket_mock_fuzz.h" @@ -25,6 +26,7 @@ #include using namespace std; +using android::base::unique_fd; const size_t kYearMin = 2000; const size_t kYearMax = 2127; @@ -255,7 +257,7 @@ void FastbootFuzzer::InvokeVendorBootImgUtils(const uint8_t* data, size_t size) uint64_t ramdisk_size = fdp_->ConsumeBool() ? content_ramdisk_fd.size() : fdp_->ConsumeIntegral(); (void)replace_vendor_ramdisk(vendor_boot_fd, vendor_boot_size, ramdisk_name, ramdisk_fd, - ramdisk_size); + ramdisk_size, unique_fd(-1), 0); close(vendor_boot_fd); close(ramdisk_fd); } diff --git a/fastboot/testdata/Android.bp b/fastboot/testdata/Android.bp index a490fe270..47bf0959e 100644 --- a/fastboot/testdata/Android.bp +++ b/fastboot/testdata/Android.bp @@ -40,6 +40,14 @@ genrule { cmd: "$(location fastboot_gen_rand) --seed dtb --length 1024 > $(out)", } +// Fake dtb image for replacement. +genrule { + name: "fastboot_test_dtb_replace", + defaults: ["fastboot_test_data_gen_defaults"], + out: ["dtb_replace.img"], + cmd: "$(location fastboot_gen_rand) --seed dtb --length 2048 > $(out)", +} + // Fake bootconfig image. genrule { name: "fastboot_test_bootconfig", diff --git a/fastboot/vendor_boot_img_utils.cpp b/fastboot/vendor_boot_img_utils.cpp index 9f05253c0..da547f1bf 100644 --- a/fastboot/vendor_boot_img_utils.cpp +++ b/fastboot/vendor_boot_img_utils.cpp @@ -209,7 +209,8 @@ inline uint32_t round_up(uint32_t value, uint32_t page_size) { // Replace the vendor ramdisk as a whole. [[nodiscard]] Result replace_default_vendor_ramdisk(const std::string& vendor_boot, - const std::string& new_ramdisk) { + const std::string& new_ramdisk, + const std::string& new_dtb) { if (auto res = check_vendor_boot_hdr(vendor_boot, 3); !res.ok()) return res.error(); auto hdr = reinterpret_cast(vendor_boot.data()); auto hdr_size = get_vendor_boot_header_size(hdr); @@ -244,8 +245,19 @@ inline uint32_t round_up(uint32_t value, uint32_t page_size) { return res.error(); if (auto res = updater.CheckOffset(o + p, o + new_p); !res.ok()) return res.error(); - // Copy DTB (Q bytes). - if (auto res = updater.Copy(q); !res.ok()) return res.error(); + // Copy DTB (Q bytes). Replace if a new one was provided. + new_hdr->dtb_size = !new_dtb.empty() ? new_dtb.size() : hdr->dtb_size; + const uint32_t new_q = round_up(new_hdr->dtb_size, new_hdr->page_size); + if (new_dtb.empty()) { + if (auto res = updater.Copy(q); !res.ok()) return res.error(); + } else { + if (auto res = updater.Replace(hdr->dtb_size, new_dtb); !res.ok()) return res.error(); + if (auto res = updater.Skip(q - hdr->dtb_size, new_q - new_hdr->dtb_size); !res.ok()) + return res.error(); + } + if (auto res = updater.CheckOffset(o + p + q, o + new_p + new_q); !res.ok()) { + return res.error(); + } if (new_hdr->header_version >= 4) { auto hdr_v4 = static_cast(hdr); @@ -256,7 +268,7 @@ inline uint32_t round_up(uint32_t value, uint32_t page_size) { auto new_hdr_v4 = static_cast(new_hdr); auto new_r = round_up(new_hdr_v4->vendor_ramdisk_table_size, new_hdr->page_size); if (auto res = updater.Skip(r, new_r); !res.ok()) return res.error(); - if (auto res = updater.CheckOffset(o + p + q + r, o + new_p + q + new_r); !res.ok()) + if (auto res = updater.CheckOffset(o + p + q + r, o + new_p + new_q + new_r); !res.ok()) return res.error(); // Replace table with single entry representing the full ramdisk. @@ -303,7 +315,8 @@ inline uint32_t round_up(uint32_t value, uint32_t page_size) { // replace it with the content of |new_ramdisk|. [[nodiscard]] Result replace_vendor_ramdisk_fragment(const std::string& ramdisk_name, const std::string& vendor_boot, - const std::string& new_ramdisk) { + const std::string& new_ramdisk, + const std::string& new_dtb) { if (auto res = check_vendor_boot_hdr(vendor_boot, 4); !res.ok()) return res.error(); auto hdr = reinterpret_cast(vendor_boot.data()); auto hdr_size = get_vendor_boot_header_size(hdr); @@ -368,8 +381,19 @@ inline uint32_t round_up(uint32_t value, uint32_t page_size) { return res.error(); if (auto res = updater.CheckOffset(o + p, o + new_p); !res.ok()) return res.error(); - // Copy DTB (Q bytes). - if (auto res = updater.Copy(q); !res.ok()) return res.error(); + // Copy DTB (Q bytes). Replace if a new one was provided. + new_hdr->dtb_size = !new_dtb.empty() ? new_dtb.size() : hdr->dtb_size; + const uint32_t new_q = round_up(new_hdr->dtb_size, new_hdr->page_size); + if (new_dtb.empty()) { + if (auto res = updater.Copy(q); !res.ok()) return res.error(); + } else { + if (auto res = updater.Replace(hdr->dtb_size, new_dtb); !res.ok()) return res.error(); + if (auto res = updater.Skip(q - hdr->dtb_size, new_q - new_hdr->dtb_size); !res.ok()) + return res.error(); + } + if (auto res = updater.CheckOffset(o + p + q, o + new_p + new_q); !res.ok()) { + return res.error(); + } // Copy table, but with corresponding entries modified, including: // - ramdisk_size of the entry replaced @@ -392,7 +416,7 @@ inline uint32_t round_up(uint32_t value, uint32_t page_size) { hdr->vendor_ramdisk_table_entry_size); !res.ok()) return res.error(); - if (auto res = updater.CheckOffset(o + p + q + r, o + new_p + q + r); !res.ok()) + if (auto res = updater.CheckOffset(o + p + q + r, o + new_p + new_q + r); !res.ok()) return res.error(); // Copy bootconfig (S bytes). @@ -404,11 +428,11 @@ inline uint32_t round_up(uint32_t value, uint32_t page_size) { } // namespace -[[nodiscard]] Result replace_vendor_ramdisk(android::base::borrowed_fd vendor_boot_fd, - uint64_t vendor_boot_size, - const std::string& ramdisk_name, - android::base::borrowed_fd new_ramdisk_fd, - uint64_t new_ramdisk_size) { +[[nodiscard]] Result replace_vendor_ramdisk( + android::base::borrowed_fd vendor_boot_fd, uint64_t vendor_boot_size, + const std::string& ramdisk_name, android::base::borrowed_fd new_ramdisk_fd, + uint64_t new_ramdisk_size, android::base::borrowed_fd new_dtb_fd, uint64_t new_dtb_size) { + Result new_dtb = {""}; if (new_ramdisk_size > std::numeric_limits::max()) { return Errorf("New vendor ramdisk is too big"); } @@ -417,12 +441,17 @@ inline uint32_t round_up(uint32_t value, uint32_t page_size) { if (!vendor_boot.ok()) return vendor_boot.error(); auto new_ramdisk = load_file(new_ramdisk_fd, new_ramdisk_size, "new vendor ramdisk"); if (!new_ramdisk.ok()) return new_ramdisk.error(); + if (new_dtb_size > 0 && new_dtb_fd >= 0) { + new_dtb = load_file(new_dtb_fd, new_dtb_size, "new dtb"); + if (!new_dtb.ok()) return new_dtb.error(); + } Result new_vendor_boot; if (ramdisk_name == "default") { - new_vendor_boot = replace_default_vendor_ramdisk(*vendor_boot, *new_ramdisk); + new_vendor_boot = replace_default_vendor_ramdisk(*vendor_boot, *new_ramdisk, *new_dtb); } else { - new_vendor_boot = replace_vendor_ramdisk_fragment(ramdisk_name, *vendor_boot, *new_ramdisk); + new_vendor_boot = + replace_vendor_ramdisk_fragment(ramdisk_name, *vendor_boot, *new_ramdisk, *new_dtb); } if (!new_vendor_boot.ok()) return new_vendor_boot.error(); if (auto res = store_file(vendor_boot_fd, *new_vendor_boot, "new vendor boot image"); !res.ok()) diff --git a/fastboot/vendor_boot_img_utils.h b/fastboot/vendor_boot_img_utils.h index 0b702bc4d..0ca78dae2 100644 --- a/fastboot/vendor_boot_img_utils.h +++ b/fastboot/vendor_boot_img_utils.h @@ -31,4 +31,4 @@ [[nodiscard]] android::base::Result replace_vendor_ramdisk( android::base::borrowed_fd vendor_boot_fd, uint64_t vendor_boot_size, const std::string& ramdisk_name, android::base::borrowed_fd new_ramdisk_fd, - uint64_t new_ramdisk_size); + uint64_t new_ramdisk_size, android::base::borrowed_fd new_dtb_fd, uint64_t new_dtb_size); diff --git a/fastboot/vendor_boot_img_utils_test.cpp b/fastboot/vendor_boot_img_utils_test.cpp index 81072705d..841e532ac 100644 --- a/fastboot/vendor_boot_img_utils_test.cpp +++ b/fastboot/vendor_boot_img_utils_test.cpp @@ -241,6 +241,7 @@ RepackVendorBootImgTestEnv* env = nullptr; struct RepackVendorBootImgTestParam { std::string vendor_boot_file_name; + std::string dtb_file_name; uint32_t expected_header_version; friend std::ostream& operator<<(std::ostream& os, const RepackVendorBootImgTestParam& param) { return os << param.vendor_boot_file_name; @@ -252,22 +253,50 @@ class RepackVendorBootImgTest : public ::testing::TestWithParam(GetParam().vendor_boot_file_name); ASSERT_RESULT_OK(vboot->Open()); + + if (!GetParam().dtb_file_name.empty()) { + dtb_replacement = std::make_unique(GetParam().dtb_file_name); + ASSERT_RESULT_OK(dtb_replacement->Open()); + } } std::unique_ptr vboot; + std::unique_ptr dtb_replacement; }; TEST_P(RepackVendorBootImgTest, InvalidSize) { - EXPECT_ERROR(replace_vendor_ramdisk(vboot->fd(), vboot->size() + 1, "default", - env->replace->fd(), env->replace->size()), - HasSubstr("Size of vendor boot does not match")); - EXPECT_ERROR(replace_vendor_ramdisk(vboot->fd(), vboot->size(), "default", env->replace->fd(), - env->replace->size() + 1), - HasSubstr("Size of new vendor ramdisk does not match")); + EXPECT_ERROR( + replace_vendor_ramdisk(vboot->fd(), vboot->size() + 1, "default", env->replace->fd(), + env->replace->size(), + !GetParam().dtb_file_name.empty() ? dtb_replacement->fd() + : android::base::unique_fd(-1), + !GetParam().dtb_file_name.empty() ? dtb_replacement->size() : 0), + HasSubstr("Size of vendor boot does not match")); + EXPECT_ERROR( + replace_vendor_ramdisk(vboot->fd(), vboot->size(), "default", env->replace->fd(), + env->replace->size() + 1, + !GetParam().dtb_file_name.empty() ? dtb_replacement->fd() + : android::base::unique_fd(-1), + !GetParam().dtb_file_name.empty() ? dtb_replacement->size() : 0), + HasSubstr("Size of new vendor ramdisk does not match")); + if (!GetParam().dtb_file_name.empty()) { + EXPECT_ERROR(replace_vendor_ramdisk(vboot->fd(), vboot->size(), "default", + env->replace->fd(), env->replace->size(), + dtb_replacement->fd(), dtb_replacement->size() + 1), + HasSubstr("Size of new dtb does not match")); + } + EXPECT_ERROR( + replace_vendor_ramdisk( + vboot->fd(), vboot->size(), "default", env->replace->fd(), env->replace->size(), + android::base::unique_fd(std::numeric_limits::max()), 1), + HasSubstr("Can't seek to the beginning of new dtb image")); } TEST_P(RepackVendorBootImgTest, ReplaceUnknown) { - auto res = replace_vendor_ramdisk(vboot->fd(), vboot->size(), "unknown", env->replace->fd(), - env->replace->size()); + auto res = replace_vendor_ramdisk( + vboot->fd(), vboot->size(), "unknown", env->replace->fd(), env->replace->size(), + !GetParam().dtb_file_name.empty() ? dtb_replacement->fd() + : android::base::unique_fd(-1), + !GetParam().dtb_file_name.empty() ? dtb_replacement->size() : 0); if (GetParam().expected_header_version == 3) { EXPECT_ERROR(res, Eq("Require vendor boot header V4 but is V3")); } else if (GetParam().expected_header_version == 4) { @@ -279,8 +308,11 @@ TEST_P(RepackVendorBootImgTest, ReplaceDefault) { auto old_content = vboot->Read(); ASSERT_RESULT_OK(old_content); - ASSERT_RESULT_OK(replace_vendor_ramdisk(vboot->fd(), vboot->size(), "default", - env->replace->fd(), env->replace->size())); + ASSERT_RESULT_OK(replace_vendor_ramdisk( + vboot->fd(), vboot->size(), "default", env->replace->fd(), env->replace->size(), + !GetParam().dtb_file_name.empty() ? dtb_replacement->fd() + : android::base::unique_fd(-1), + !GetParam().dtb_file_name.empty() ? dtb_replacement->size() : 0)); EXPECT_RESULT(vboot->fsize(), vboot->size()) << "File size should not change after repack"; auto new_content_res = vboot->Read(); @@ -291,14 +323,23 @@ TEST_P(RepackVendorBootImgTest, ReplaceDefault) { ASSERT_EQ(0, memcmp(VENDOR_BOOT_MAGIC, hdr->magic, VENDOR_BOOT_MAGIC_SIZE)); ASSERT_EQ(GetParam().expected_header_version, hdr->header_version); EXPECT_EQ(hdr->vendor_ramdisk_size, env->replace->size()); - EXPECT_EQ(hdr->dtb_size, env->dtb->size()); + if (GetParam().dtb_file_name.empty()) { + EXPECT_EQ(hdr->dtb_size, env->dtb->size()); + } else { + EXPECT_EQ(hdr->dtb_size, dtb_replacement->size()); + } auto o = round_up(sizeof(vendor_boot_img_hdr_v3), hdr->page_size); auto p = round_up(hdr->vendor_ramdisk_size, hdr->page_size); auto q = round_up(hdr->dtb_size, hdr->page_size); EXPECT_THAT(new_content.substr(o, p), IsPadded(env->replace_content)); - EXPECT_THAT(new_content.substr(o + p, q), IsPadded(env->dtb_content)); + if (GetParam().dtb_file_name.empty()) { + EXPECT_THAT(new_content.substr(o + p, q), IsPadded(env->dtb_content)); + } else { + auto dtb_content_res = dtb_replacement->Read(); + EXPECT_THAT(new_content.substr(o + p, q), IsPadded(*dtb_content_res)); + } if (hdr->header_version < 4) return; @@ -321,11 +362,17 @@ TEST_P(RepackVendorBootImgTest, ReplaceDefault) { INSTANTIATE_TEST_SUITE_P( RepackVendorBootImgTest, RepackVendorBootImgTest, - ::testing::Values(RepackVendorBootImgTestParam{"vendor_boot_v3.img", 3}, - RepackVendorBootImgTestParam{"vendor_boot_v4_with_frag.img", 4}, - RepackVendorBootImgTestParam{"vendor_boot_v4_without_frag.img", 4}), + ::testing::Values(RepackVendorBootImgTestParam{"vendor_boot_v3.img", "", 3}, + RepackVendorBootImgTestParam{"vendor_boot_v4_with_frag.img", "", 4}, + RepackVendorBootImgTestParam{"vendor_boot_v4_without_frag.img", "", 4}, + RepackVendorBootImgTestParam{"vendor_boot_v4_with_frag.img", + "dtb_replace.img", 4}, + RepackVendorBootImgTestParam{"vendor_boot_v4_without_frag.img", + "dtb_replace.img", 4}), [](const auto& info) { - return android::base::StringReplace(info.param.vendor_boot_file_name, ".", "_", false); + std::string test_name = + android::base::StringReplace(info.param.vendor_boot_file_name, ".", "_", false); + return test_name + (!info.param.dtb_file_name.empty() ? "_replace_dtb" : ""); }); std::string_view GetRamdiskName(const vendor_ramdisk_table_entry_v4* entry) { @@ -368,7 +415,8 @@ TEST_P(RepackVendorBootImgTestV4, Replace) { ASSERT_RESULT_OK(old_content); ASSERT_RESULT_OK(replace_vendor_ramdisk(vboot->fd(), vboot->size(), replace_ramdisk_name, - env->replace->fd(), env->replace->size())); + env->replace->fd(), env->replace->size(), + android::base::unique_fd(-1), 0)); EXPECT_RESULT(vboot->fsize(), vboot->size()) << "File size should not change after repack"; auto new_content_res = vboot->Read();