1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "host/commands/launch/boot_image_unpacker.h" 18 19 #include <string.h> 20 #include <unistd.h> 21 22 #include <sstream> 23 24 #include <glog/logging.h> 25 26 #include "common/libs/utils/subprocess.h" 27 #include "host/commands/launch/bootimg.h" 28 29 namespace cvd { 30 31 namespace { 32 33 // Extracts size bytes from file, starting at offset bytes from the beginning to 34 // path. 35 bool ExtractFile(SharedFD source, off_t offset, size_t size, 36 const std::string& path) { 37 auto dest = SharedFD::Open(path.c_str(), O_CREAT | O_RDWR, 0755); 38 if (!dest->IsOpen()) { 39 LOG(ERROR) << "Unable to open " << path; 40 return false; 41 } 42 auto off = source->LSeek(offset, SEEK_SET); 43 if (off != offset) { 44 LOG(ERROR) << "Failed to lseek: " << source->StrError(); 45 return false; 46 } 47 return dest->CopyFrom(*source, size); 48 } 49 } // namespace 50 51 std::unique_ptr<BootImageUnpacker> BootImageUnpacker::FromImage( 52 const std::string& path) { 53 auto boot_img = SharedFD::Open(path.c_str(), O_RDONLY); 54 if (!boot_img->IsOpen()) { 55 LOG(ERROR) << "Unable to open boot image (" << path 56 << "): " << boot_img->StrError(); 57 return nullptr; 58 } 59 boot_img_hdr header; 60 auto bytes_read = boot_img->Read(&header, sizeof(header)); 61 if (bytes_read != sizeof(header)) { 62 LOG(ERROR) << "Error reading boot image header"; 63 return nullptr; 64 } 65 66 std::ostringstream cmdline; 67 cmdline << reinterpret_cast<char*>(&header.cmdline[0]); 68 if (header.extra_cmdline[0] != '\0') { 69 cmdline << " "; 70 cmdline << reinterpret_cast<char*>(&header.extra_cmdline[0]); 71 } 72 73 uint32_t page_size = header.page_size; 74 // See system/core/mkbootimg/include/mkbootimg/bootimg.h for the origin of 75 // these offset calculations 76 uint32_t kernel_offset = page_size; 77 uint32_t ramdisk_offset = 78 kernel_offset + 79 ((header.kernel_size + page_size - 1) / page_size) * page_size; 80 81 std::unique_ptr<BootImageUnpacker> ret(new BootImageUnpacker( 82 boot_img, cmdline.str(), header.kernel_size, kernel_offset, 83 header.ramdisk_size, ramdisk_offset)); 84 85 return ret; 86 } 87 88 std::string BootImageUnpacker::kernel_cmdline() const { 89 return kernel_cmdline_; 90 } 91 92 bool BootImageUnpacker::ExtractKernelImage(const std::string& path) const { 93 if (kernel_image_size_ == 0) return false; 94 return ExtractFile(boot_image_, kernel_image_offset_, kernel_image_size_, 95 path); 96 } 97 bool BootImageUnpacker::ExtractRamdiskImage(const std::string& path) const { 98 if (ramdisk_image_size_ == 0) return false; 99 return ExtractFile(boot_image_, ramdisk_image_offset_, ramdisk_image_size_, 100 path); 101 } 102 103 bool BootImageUnpacker::Unpack(const std::string& ramdisk_image_path, 104 const std::string& kernel_image_path) { 105 if (HasRamdiskImage()) { 106 if (!ExtractRamdiskImage(ramdisk_image_path)) { 107 LOG(ERROR) << "Error extracting ramdisk from boot image"; 108 return false; 109 } 110 } 111 if (!kernel_image_path.empty()) { 112 if (HasKernelImage()) { 113 if (!ExtractKernelImage(kernel_image_path)) { 114 LOG(ERROR) << "Error extracting kernel from boot image"; 115 return false; 116 } 117 } else { 118 LOG(ERROR) << "No kernel found on boot image"; 119 return false; 120 } 121 } 122 return true; 123 } 124 125 } // namespace cvd 126