Allow parsing boot image with header version 3
The boot image header version no longer contains a page_size field;
and it assumes the block size is always 4096. Also, the order of the
header fields are different in version 3 from the previous versions.
While the position of "header_version" is fixed at position #9 across
all image headers.
This cl handles header version 3 separately from the previous versions.
Also create a stub implementation since the parsing of boot image is only
used in android.
Bug: 152371989
Test: unit tests, generate incremental OTA for the affected package, build with stub file
Change-Id: Iea2145e0c234216f9ee42c571bd53dc93b4b9d2e
diff --git a/Android.bp b/Android.bp
index 21ba507..e3116f5 100644
--- a/Android.bp
+++ b/Android.bp
@@ -492,6 +492,9 @@
"update_metadata-protos_exports",
],
+ header_libs: [
+ "bootimg_headers",
+ ],
static_libs: [
"libavb",
"libbrotli",
diff --git a/payload_generator/boot_img_filesystem.cc b/payload_generator/boot_img_filesystem.cc
index 19de410..89b175e 100644
--- a/payload_generator/boot_img_filesystem.cc
+++ b/payload_generator/boot_img_filesystem.cc
@@ -17,6 +17,7 @@
#include "update_engine/payload_generator/boot_img_filesystem.h"
#include <base/logging.h>
+#include <bootimg.h>
#include <brillo/secure_blob.h>
#include <puffin/utils.h>
@@ -35,16 +36,61 @@
if (filename.empty())
return nullptr;
- brillo::Blob header;
- if (!utils::ReadFileChunk(filename, 0, sizeof(boot_img_hdr), &header) ||
- header.size() != sizeof(boot_img_hdr) ||
- memcmp(header.data(), BOOT_MAGIC, BOOT_MAGIC_SIZE) != 0) {
+ if (brillo::Blob header_magic;
+ !utils::ReadFileChunk(filename, 0, BOOT_MAGIC_SIZE, &header_magic) ||
+ memcmp(header_magic.data(), BOOT_MAGIC, BOOT_MAGIC_SIZE) != 0) {
+ return nullptr;
+ }
+
+ // The order of image header fields are different in version 3 from the
+ // previous versions. But the position of "header_version" is fixed at #9
+ // across all image headers.
+ // See details in system/tools/mkbootimg/include/bootimg/bootimg.h
+ constexpr size_t header_version_offset =
+ BOOT_MAGIC_SIZE + 8 * sizeof(uint32_t);
+ brillo::Blob header_version_blob;
+ if (!utils::ReadFileChunk(filename,
+ header_version_offset,
+ sizeof(uint32_t),
+ &header_version_blob)) {
+ return nullptr;
+ }
+ uint32_t header_version =
+ *reinterpret_cast<uint32_t*>(header_version_blob.data());
+ if (header_version > 3) {
+ LOG(WARNING) << "Boot image header version " << header_version
+ << " isn't supported for parsing";
+ return nullptr;
+ }
+
+ // Read the bytes of boot image header based on the header version.
+ size_t header_size =
+ header_version == 3 ? sizeof(boot_img_hdr_v3) : sizeof(boot_img_hdr_v0);
+ brillo::Blob header_blob;
+ if (!utils::ReadFileChunk(filename, 0, header_size, &header_blob)) {
return nullptr;
}
unique_ptr<BootImgFilesystem> result(new BootImgFilesystem());
result->filename_ = filename;
- memcpy(&result->hdr_, header.data(), header.size());
+ if (header_version < 3) {
+ auto hdr_v0 = reinterpret_cast<boot_img_hdr_v0*>(header_blob.data());
+ CHECK_EQ(0, memcmp(hdr_v0->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE));
+ CHECK_LT(hdr_v0->header_version, 3u);
+ result->kernel_size_ = hdr_v0->kernel_size;
+ result->ramdisk_size_ = hdr_v0->ramdisk_size;
+ result->page_size_ = hdr_v0->page_size;
+ } else {
+ auto hdr_v3 = reinterpret_cast<boot_img_hdr_v3*>(header_blob.data());
+ CHECK_EQ(0, memcmp(hdr_v3->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE));
+ CHECK_EQ(3u, hdr_v3->header_version);
+ result->kernel_size_ = hdr_v3->kernel_size;
+ result->ramdisk_size_ = hdr_v3->ramdisk_size;
+ result->page_size_ = 4096;
+ }
+
+ CHECK_GT(result->page_size_, 0u);
+
return result;
}
@@ -87,13 +133,13 @@
files->clear();
const uint64_t file_size = utils::FileSize(filename_);
// The first page is header.
- uint64_t offset = hdr_.page_size;
- if (hdr_.kernel_size > 0 && offset + hdr_.kernel_size <= file_size) {
- files->emplace_back(GetFile("<kernel>", offset, hdr_.kernel_size));
+ uint64_t offset = page_size_;
+ if (kernel_size_ > 0 && offset + kernel_size_ <= file_size) {
+ files->emplace_back(GetFile("<kernel>", offset, kernel_size_));
}
- offset += utils::RoundUp(hdr_.kernel_size, hdr_.page_size);
- if (hdr_.ramdisk_size > 0 && offset + hdr_.ramdisk_size <= file_size) {
- files->emplace_back(GetFile("<ramdisk>", offset, hdr_.ramdisk_size));
+ offset += utils::RoundUp(kernel_size_, page_size_);
+ if (ramdisk_size_ > 0 && offset + ramdisk_size_ <= file_size) {
+ files->emplace_back(GetFile("<ramdisk>", offset, ramdisk_size_));
}
return true;
}
diff --git a/payload_generator/boot_img_filesystem.h b/payload_generator/boot_img_filesystem.h
index 87725d4..61f755c 100644
--- a/payload_generator/boot_img_filesystem.h
+++ b/payload_generator/boot_img_filesystem.h
@@ -52,23 +52,9 @@
// The boot.img file path.
std::string filename_;
-// https://android.googlesource.com/platform/system/core/+/master/mkbootimg/include/bootimg/bootimg.h
-#define BOOT_MAGIC "ANDROID!"
-#define BOOT_MAGIC_SIZE 8
- struct boot_img_hdr {
- // Must be BOOT_MAGIC.
- 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 */
- } __attribute__((packed));
- // The boot image header.
- boot_img_hdr hdr_;
+ uint32_t kernel_size_; /* size in bytes */
+ uint32_t ramdisk_size_; /* size in bytes */
+ uint32_t page_size_; /* flash page size we assume */
DISALLOW_COPY_AND_ASSIGN(BootImgFilesystem);
};
diff --git a/payload_generator/boot_img_filesystem_stub.cc b/payload_generator/boot_img_filesystem_stub.cc
new file mode 100644
index 0000000..4928fa1
--- /dev/null
+++ b/payload_generator/boot_img_filesystem_stub.cc
@@ -0,0 +1,48 @@
+//
+// Copyright (C) 2020 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 "update_engine/payload_generator/boot_img_filesystem.h"
+
+namespace chromeos_update_engine {
+std::unique_ptr<BootImgFilesystem> BootImgFilesystem::CreateFromFile(
+ const std::string& /* filename */) {
+ return nullptr;
+}
+
+size_t BootImgFilesystem::GetBlockSize() const {
+ return 4096;
+}
+
+size_t BootImgFilesystem::GetBlockCount() const {
+ return 0;
+}
+
+FilesystemInterface::File BootImgFilesystem::GetFile(
+ const std::string& /* name */,
+ uint64_t /* offset */,
+ uint64_t /* size */) const {
+ return {};
+}
+
+bool BootImgFilesystem::GetFiles(std::vector<File>* /* files */) const {
+ return false;
+}
+
+bool BootImgFilesystem::LoadSettings(brillo::KeyValueStore* /* store */) const {
+ return false;
+}
+
+} // namespace chromeos_update_engine
diff --git a/payload_generator/boot_img_filesystem_unittest.cc b/payload_generator/boot_img_filesystem_unittest.cc
index b1e0d99..0b115e0 100644
--- a/payload_generator/boot_img_filesystem_unittest.cc
+++ b/payload_generator/boot_img_filesystem_unittest.cc
@@ -18,6 +18,7 @@
#include <vector>
+#include <bootimg.h>
#include <brillo/secure_blob.h>
#include <gtest/gtest.h>
@@ -32,18 +33,32 @@
class BootImgFilesystemTest : public ::testing::Test {
protected:
brillo::Blob GetBootImg(const brillo::Blob& kernel,
- const brillo::Blob& ramdisk) {
+ const brillo::Blob& ramdisk,
+ bool header_version3 = false) {
brillo::Blob boot_img(16 * 1024);
- BootImgFilesystem::boot_img_hdr hdr;
- memcpy(hdr.magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);
- hdr.kernel_size = kernel.size();
- hdr.ramdisk_size = ramdisk.size();
- hdr.page_size = 4096;
+ constexpr uint32_t page_size = 4096;
+
size_t offset = 0;
- memcpy(boot_img.data() + offset, &hdr, sizeof(hdr));
- offset += utils::RoundUp(sizeof(hdr), hdr.page_size);
+ if (header_version3) {
+ boot_img_hdr_v3 hdr_v3{};
+ memcpy(hdr_v3.magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);
+ hdr_v3.kernel_size = kernel.size();
+ hdr_v3.ramdisk_size = ramdisk.size();
+ hdr_v3.header_version = 3;
+ memcpy(boot_img.data() + offset, &hdr_v3, sizeof(hdr_v3));
+ offset += utils::RoundUp(sizeof(hdr_v3), page_size);
+ } else {
+ boot_img_hdr_v0 hdr_v0{};
+ memcpy(hdr_v0.magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);
+ hdr_v0.kernel_size = kernel.size();
+ hdr_v0.ramdisk_size = ramdisk.size();
+ hdr_v0.page_size = page_size;
+ hdr_v0.header_version = 0;
+ memcpy(boot_img.data() + offset, &hdr_v0, sizeof(hdr_v0));
+ offset += utils::RoundUp(sizeof(hdr_v0), page_size);
+ }
memcpy(boot_img.data() + offset, kernel.data(), kernel.size());
- offset += utils::RoundUp(kernel.size(), hdr.page_size);
+ offset += utils::RoundUp(kernel.size(), page_size);
memcpy(boot_img.data() + offset, ramdisk.data(), ramdisk.size());
return boot_img;
}
@@ -76,6 +91,31 @@
EXPECT_TRUE(files[1].deflates.empty());
}
+TEST_F(BootImgFilesystemTest, ImageHeaderVersion3) {
+ test_utils::WriteFileVector(
+ boot_file_.path(),
+ GetBootImg(brillo::Blob(1000, 'k'), brillo::Blob(5000, 'r'), true));
+ unique_ptr<BootImgFilesystem> fs =
+ BootImgFilesystem::CreateFromFile(boot_file_.path());
+ EXPECT_NE(nullptr, fs);
+
+ vector<FilesystemInterface::File> files;
+ EXPECT_TRUE(fs->GetFiles(&files));
+ ASSERT_EQ(2u, files.size());
+
+ EXPECT_EQ("<kernel>", files[0].name);
+ EXPECT_EQ(1u, files[0].extents.size());
+ EXPECT_EQ(1u, files[0].extents[0].start_block());
+ EXPECT_EQ(1u, files[0].extents[0].num_blocks());
+ EXPECT_TRUE(files[0].deflates.empty());
+
+ EXPECT_EQ("<ramdisk>", files[1].name);
+ EXPECT_EQ(1u, files[1].extents.size());
+ EXPECT_EQ(2u, files[1].extents[0].start_block());
+ EXPECT_EQ(2u, files[1].extents[0].num_blocks());
+ EXPECT_TRUE(files[1].deflates.empty());
+}
+
TEST_F(BootImgFilesystemTest, BadImageTest) {
brillo::Blob boot_img = GetBootImg({}, {});
boot_img[7] = '?';