/* * 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/assemble_cvd/boot_image_unpacker.h" #include #include #include #include #include #include "common/libs/utils/subprocess.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::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 (" << boot_image_path << "): " << boot_img->StrError(); return nullptr; } 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; } 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; } std::ostringstream cmdline; cmdline << reinterpret_cast(&header.cmdline[0]); if (vboot_header.cmdline[0] != '\0') { cmdline << " "; cmdline << reinterpret_cast(&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 ret(new BootImageUnpacker( boot_img, cmdline.str(), header.kernel_size, kernel_offset, header.ramdisk_size, ramdisk_offset, vendor_boot_img, vboot_header.vendor_ramdisk_size, vendor_ramdisk_offset)); return ret; } std::string BootImageUnpacker::kernel_cmdline() const { return kernel_cmdline_; } 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); } 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)) { LOG(ERROR) << "Error extracting ramdisk from boot image"; 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)) { LOG(ERROR) << "Error extracting kernel from boot image"; return false; } } else { LOG(ERROR) << "No kernel found on boot image"; return false; } } return true; } } // namespace cvd