• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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/assemble_cvd/boot_image_utils.h"
18 #include "host/libs/config/cuttlefish_config.h"
19 
20 #include <string.h>
21 #include <unistd.h>
22 
23 #include <fstream>
24 #include <sstream>
25 
26 #include <android-base/logging.h>
27 #include <android-base/strings.h>
28 
29 #include "common/libs/utils/files.h"
30 #include "common/libs/utils/subprocess.h"
31 
32 const char TMP_EXTENSION[] = ".tmp";
33 const char CPIO_EXT[] = ".cpio";
34 const char TMP_RD_DIR[] = "stripped_ramdisk_dir";
35 const char STRIPPED_RD[] = "stripped_ramdisk";
36 const char CONCATENATED_VENDOR_RAMDISK[] = "concatenated_vendor_ramdisk";
37 namespace cuttlefish {
38 namespace {
ExtractValue(const std::string & dictionary,const std::string & key)39 std::string ExtractValue(const std::string& dictionary, const std::string& key) {
40   std::size_t index = dictionary.find(key);
41   if (index != std::string::npos) {
42     std::size_t end_index = dictionary.find('\n', index + key.length());
43     if (end_index != std::string::npos) {
44       return dictionary.substr(index + key.length(),
45           end_index - index - key.length());
46     }
47   }
48   return "";
49 }
50 
51 // Though it is just as fast to overwrite the existing boot images with the newly generated ones,
52 // the cuttlefish composite disk generator checks the age of each of the components and
53 // regenerates the disk outright IF any one of the components is younger/newer than the current
54 // composite disk. If this file overwrite occurs, that condition is fulfilled. This action then
55 // causes data in the userdata partition from previous boots to be lost (which is not expected by
56 // the user if they've been booting the same kernel/ramdisk combination repeatedly).
57 // Consequently, the file is checked for differences and ONLY overwritten if there is a diff.
DeleteTmpFileIfNotChanged(const std::string & tmp_file,const std::string & current_file)58 bool DeleteTmpFileIfNotChanged(const std::string& tmp_file, const std::string& current_file) {
59   if (!FileExists(current_file) ||
60       ReadFile(current_file) != ReadFile(tmp_file)) {
61     if (!RenameFile(tmp_file, current_file)) {
62       LOG(ERROR) << "Unable to delete " << current_file;
63       return false;
64     }
65     LOG(DEBUG) << "Updated " << current_file;
66   } else {
67     LOG(DEBUG) << "Didn't update " << current_file;
68     RemoveFile(tmp_file);
69   }
70 
71   return true;
72 }
73 
RepackVendorRamdisk(const std::string & kernel_modules_ramdisk_path,const std::string & original_ramdisk_path,const std::string & new_ramdisk_path,const std::string & build_dir)74 void RepackVendorRamdisk(const std::string& kernel_modules_ramdisk_path,
75                          const std::string& original_ramdisk_path,
76                          const std::string& new_ramdisk_path,
77                          const std::string& build_dir) {
78   int success = execute({"/bin/bash", "-c", HostBinaryPath("lz4") + " -c -d -l " +
79                         original_ramdisk_path + " > " + original_ramdisk_path + CPIO_EXT});
80   CHECK(success == 0) << "Unable to run lz4. Exited with status " << success;
81 
82   const std::string ramdisk_stage_dir = build_dir + "/" + TMP_RD_DIR;
83   success =
84       mkdir(ramdisk_stage_dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
85   CHECK(success == 0) << "Could not mkdir \"" << ramdisk_stage_dir
86                       << "\", error was " << strerror(errno);
87 
88   success = execute(
89       {"/bin/bash", "-c",
90        "(cd " + ramdisk_stage_dir + " && while " + HostBinaryPath("toybox") +
91            " cpio -idu; do :; done) < " + original_ramdisk_path + CPIO_EXT});
92   CHECK(success == 0) << "Unable to run cd or cpio. Exited with status "
93                       << success;
94 
95   success = execute({"rm", "-rf", ramdisk_stage_dir + "/lib/modules"});
96   CHECK(success == 0) << "Could not rmdir \"lib/modules\" in TMP_RD_DIR. "
97                       << "Exited with status " << success;
98 
99   const std::string stripped_ramdisk_path = build_dir + "/" + STRIPPED_RD;
100   success = execute({"/bin/bash", "-c",
101                      HostBinaryPath("mkbootfs") + " " + ramdisk_stage_dir +
102                          " > " + stripped_ramdisk_path + CPIO_EXT});
103   CHECK(success == 0) << "Unable to run cd or cpio. Exited with status "
104                       << success;
105 
106   success = execute({"/bin/bash", "-c", HostBinaryPath("lz4") +
107                      " -c -l -12 --favor-decSpeed " + stripped_ramdisk_path + CPIO_EXT + " > " +
108                      stripped_ramdisk_path});
109   CHECK(success == 0) << "Unable to run lz4. Exited with status " << success;
110 
111   // Concatenates the stripped ramdisk and input ramdisk and places the result at new_ramdisk_path
112   std::ofstream final_rd(new_ramdisk_path, std::ios_base::binary | std::ios_base::trunc);
113   std::ifstream ramdisk_a(stripped_ramdisk_path, std::ios_base::binary);
114   std::ifstream ramdisk_b(kernel_modules_ramdisk_path, std::ios_base::binary);
115   final_rd << ramdisk_a.rdbuf() << ramdisk_b.rdbuf();
116 }
117 
118 }  // namespace
119 
UnpackBootImage(const std::string & boot_image_path,const std::string & unpack_dir)120 bool UnpackBootImage(const std::string& boot_image_path,
121                      const std::string& unpack_dir) {
122   auto unpack_path = HostBinaryPath("unpack_bootimg");
123   Command unpack_cmd(unpack_path);
124   unpack_cmd.AddParameter("--boot_img");
125   unpack_cmd.AddParameter(boot_image_path);
126   unpack_cmd.AddParameter("--out");
127   unpack_cmd.AddParameter(unpack_dir);
128 
129   auto output_file = SharedFD::Creat(unpack_dir + "/boot_params", 0666);
130   if (!output_file->IsOpen()) {
131     LOG(ERROR) << "Unable to create intermediate boot params file: "
132                << output_file->StrError();
133     return false;
134   }
135   unpack_cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdOut, output_file);
136 
137   int success = unpack_cmd.Start().Wait();
138   if (success != 0) {
139     LOG(ERROR) << "Unable to run unpack_bootimg. Exited with status "
140                << success;
141     return false;
142   }
143   return true;
144 }
145 
UnpackVendorBootImageIfNotUnpacked(const std::string & vendor_boot_image_path,const std::string & unpack_dir)146 bool UnpackVendorBootImageIfNotUnpacked(
147     const std::string& vendor_boot_image_path, const std::string& unpack_dir) {
148   // the vendor boot params file is created during the first unpack. If it's
149   // already there, a unpack has occurred and there's no need to repeat the
150   // process.
151   if (FileExists(unpack_dir + "/vendor_boot_params")) {
152     return true;
153   }
154 
155   auto unpack_path = HostBinaryPath("unpack_bootimg");
156   Command unpack_cmd(unpack_path);
157   unpack_cmd.AddParameter("--boot_img");
158   unpack_cmd.AddParameter(vendor_boot_image_path);
159   unpack_cmd.AddParameter("--out");
160   unpack_cmd.AddParameter(unpack_dir);
161   auto output_file = SharedFD::Creat(unpack_dir + "/vendor_boot_params", 0666);
162   if (!output_file->IsOpen()) {
163     LOG(ERROR) << "Unable to create intermediate vendor boot params file: "
164                << output_file->StrError();
165     return false;
166   }
167   unpack_cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdOut, output_file);
168   int success = unpack_cmd.Start().Wait();
169   if (success != 0) {
170     LOG(ERROR) << "Unable to run unpack_bootimg. Exited with status " << success;
171     return false;
172   }
173 
174   // Concatenates all vendor ramdisk into one single ramdisk.
175   Command concat_cmd("/bin/bash");
176   concat_cmd.AddParameter("-c");
177   concat_cmd.AddParameter("cat " + unpack_dir + "/vendor_ramdisk*");
178   auto concat_file =
179       SharedFD::Creat(unpack_dir + "/" + CONCATENATED_VENDOR_RAMDISK, 0666);
180   if (!concat_file->IsOpen()) {
181     LOG(ERROR) << "Unable to create concatenated vendor ramdisk file: "
182                << concat_file->StrError();
183     return false;
184   }
185   concat_cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdOut, concat_file);
186   success = concat_cmd.Start().Wait();
187   if (success != 0) {
188     LOG(ERROR) << "Unable to run cat. Exited with status " << success;
189     return false;
190   }
191   return true;
192 }
193 
RepackBootImage(const std::string & new_kernel_path,const std::string & boot_image_path,const std::string & new_boot_image_path,const std::string & build_dir)194 bool RepackBootImage(const std::string& new_kernel_path,
195                      const std::string& boot_image_path,
196                      const std::string& new_boot_image_path,
197                      const std::string& build_dir) {
198   if (UnpackBootImage(boot_image_path, build_dir) == false) {
199     return false;
200   }
201 
202   std::string boot_params = ReadFile(build_dir + "/boot_params");
203   auto kernel_cmdline = ExtractValue(boot_params, "command line args: ");
204   LOG(DEBUG) << "Cmdline from boot image is " << kernel_cmdline;
205 
206   auto tmp_boot_image_path = new_boot_image_path + TMP_EXTENSION;
207   auto repack_path = HostBinaryPath("mkbootimg");
208   Command repack_cmd(repack_path);
209   repack_cmd.AddParameter("--kernel");
210   repack_cmd.AddParameter(new_kernel_path);
211   repack_cmd.AddParameter("--ramdisk");
212   repack_cmd.AddParameter(build_dir + "/ramdisk");
213   repack_cmd.AddParameter("--header_version");
214   repack_cmd.AddParameter("4");
215   repack_cmd.AddParameter("--cmdline");
216   repack_cmd.AddParameter(kernel_cmdline);
217   repack_cmd.AddParameter("-o");
218   repack_cmd.AddParameter(tmp_boot_image_path);
219   int success = repack_cmd.Start().Wait();
220   if (success != 0) {
221     LOG(ERROR) << "Unable to run mkbootimg. Exited with status " << success;
222     return false;
223   }
224 
225   auto avbtool_path = HostBinaryPath("avbtool");
226   Command avb_cmd(avbtool_path);
227   avb_cmd.AddParameter("add_hash_footer");
228   avb_cmd.AddParameter("--image");
229   avb_cmd.AddParameter(tmp_boot_image_path);
230   avb_cmd.AddParameter("--partition_size");
231   avb_cmd.AddParameter(FileSize(boot_image_path));
232   avb_cmd.AddParameter("--partition_name");
233   avb_cmd.AddParameter("boot");
234   success = avb_cmd.Start().Wait();
235   if (success != 0) {
236     LOG(ERROR) << "Unable to run avbtool. Exited with status " << success;
237     return false;
238   }
239 
240   return DeleteTmpFileIfNotChanged(tmp_boot_image_path, new_boot_image_path);
241 }
242 
RepackVendorBootImage(const std::string & new_ramdisk,const std::string & vendor_boot_image_path,const std::string & new_vendor_boot_image_path,const std::string & unpack_dir,bool bootconfig_supported)243 bool RepackVendorBootImage(const std::string& new_ramdisk,
244                            const std::string& vendor_boot_image_path,
245                            const std::string& new_vendor_boot_image_path,
246                            const std::string& unpack_dir,
247                            bool bootconfig_supported) {
248   if (UnpackVendorBootImageIfNotUnpacked(vendor_boot_image_path, unpack_dir) ==
249       false) {
250     return false;
251   }
252 
253   std::string ramdisk_path;
254   if (new_ramdisk.size()) {
255     ramdisk_path = unpack_dir + "/vendor_ramdisk_repacked";
256     if (!FileExists(ramdisk_path)) {
257       RepackVendorRamdisk(new_ramdisk,
258                           unpack_dir + "/" + CONCATENATED_VENDOR_RAMDISK,
259                           ramdisk_path, unpack_dir);
260     }
261   } else {
262     ramdisk_path = unpack_dir + "/" + CONCATENATED_VENDOR_RAMDISK;
263   }
264 
265   std::string bootconfig = ReadFile(unpack_dir + "/bootconfig");
266   LOG(DEBUG) << "Bootconfig parameters from vendor boot image are "
267              << bootconfig;
268   std::string vendor_boot_params = ReadFile(unpack_dir + "/vendor_boot_params");
269   auto kernel_cmdline =
270       ExtractValue(vendor_boot_params, "vendor command line args: ") +
271       (bootconfig_supported
272            ? ""
273            : " " + android::base::StringReplace(bootconfig, "\n", " ", true));
274   if (!bootconfig_supported) {
275     // TODO(b/182417593): Until we pass the module parameters through
276     // modules.options, we pass them through bootconfig using
277     // 'kernel.<key>=<value>' But if we don't support bootconfig, we need to
278     // rename them back to the old cmdline version
279     kernel_cmdline = android::base::StringReplace(
280         kernel_cmdline, " kernel.", " ", true);
281   }
282   LOG(DEBUG) << "Cmdline from vendor boot image is " << kernel_cmdline;
283 
284   auto tmp_vendor_boot_image_path = new_vendor_boot_image_path + TMP_EXTENSION;
285   auto repack_path = HostBinaryPath("mkbootimg");
286   Command repack_cmd(repack_path);
287   repack_cmd.AddParameter("--vendor_ramdisk");
288   repack_cmd.AddParameter(ramdisk_path);
289   repack_cmd.AddParameter("--header_version");
290   repack_cmd.AddParameter("4");
291   repack_cmd.AddParameter("--vendor_cmdline");
292   repack_cmd.AddParameter(kernel_cmdline);
293   repack_cmd.AddParameter("--vendor_boot");
294   repack_cmd.AddParameter(tmp_vendor_boot_image_path);
295   repack_cmd.AddParameter("--dtb");
296   repack_cmd.AddParameter(unpack_dir + "/dtb");
297   if (bootconfig_supported) {
298     repack_cmd.AddParameter("--vendor_bootconfig");
299     repack_cmd.AddParameter(unpack_dir + "/bootconfig");
300   }
301 
302   int success = repack_cmd.Start().Wait();
303   if (success != 0) {
304     LOG(ERROR) << "Unable to run mkbootimg. Exited with status " << success;
305     return false;
306   }
307 
308   auto avbtool_path = HostBinaryPath("avbtool");
309   Command avb_cmd(avbtool_path);
310   avb_cmd.AddParameter("add_hash_footer");
311   avb_cmd.AddParameter("--image");
312   avb_cmd.AddParameter(tmp_vendor_boot_image_path);
313   avb_cmd.AddParameter("--partition_size");
314   avb_cmd.AddParameter(FileSize(vendor_boot_image_path));
315   avb_cmd.AddParameter("--partition_name");
316   avb_cmd.AddParameter("vendor_boot");
317   success = avb_cmd.Start().Wait();
318   if (success != 0) {
319     LOG(ERROR) << "Unable to run avbtool. Exited with status " << success;
320     return false;
321   }
322 
323   return DeleteTmpFileIfNotChanged(tmp_vendor_boot_image_path, new_vendor_boot_image_path);
324 }
325 
RepackVendorBootImageWithEmptyRamdisk(const std::string & vendor_boot_image_path,const std::string & new_vendor_boot_image_path,const std::string & unpack_dir,bool bootconfig_supported)326 bool RepackVendorBootImageWithEmptyRamdisk(
327     const std::string& vendor_boot_image_path,
328     const std::string& new_vendor_boot_image_path,
329     const std::string& unpack_dir, bool bootconfig_supported) {
330   auto empty_ramdisk_file =
331       SharedFD::Creat(unpack_dir + "/empty_ramdisk", 0666);
332   return RepackVendorBootImage(
333       unpack_dir + "/empty_ramdisk", vendor_boot_image_path,
334       new_vendor_boot_image_path, unpack_dir, bootconfig_supported);
335 }
336 
RepackGem5BootImage(const std::string & initrd_path,const std::string & bootconfig_path,const std::string & unpack_dir)337 void RepackGem5BootImage(const std::string& initrd_path,
338                          const std::string& bootconfig_path,
339                          const std::string& unpack_dir) {
340   // Simulate per-instance what the bootloader would usually do
341   // Since on other devices this runs every time, just do it here every time
342   std::ofstream final_rd(initrd_path,
343                          std::ios_base::binary | std::ios_base::trunc);
344 
345   std::ifstream boot_ramdisk(unpack_dir + "/ramdisk",
346                              std::ios_base::binary);
347   std::ifstream vendor_boot_ramdisk(unpack_dir +
348                                     "/concatenated_vendor_ramdisk",
349                                     std::ios_base::binary);
350 
351   std::ifstream vendor_boot_bootconfig(unpack_dir + "/bootconfig",
352                                        std::ios_base::binary |
353                                        std::ios_base::ate);
354 
355   auto vb_size = vendor_boot_bootconfig.tellg();
356   vendor_boot_bootconfig.seekg(0);
357 
358   std::ifstream persistent_bootconfig(bootconfig_path,
359                                       std::ios_base::binary |
360                                       std::ios_base::ate);
361 
362   auto pb_size = persistent_bootconfig.tellg();
363   persistent_bootconfig.seekg(0);
364 
365   // Build the bootconfig string, trim it, and write the length, checksum
366   // and trailer bytes
367 
368   std::string bootconfig =
369     "androidboot.slot_suffix=_a\n"
370     "androidboot.force_normal_boot=1\n"
371     "androidboot.verifiedbootstate=orange\n";
372   auto bootconfig_size = bootconfig.size();
373   bootconfig.resize(bootconfig_size + (uint64_t)(vb_size + pb_size), '\0');
374   vendor_boot_bootconfig.read(&bootconfig[bootconfig_size], vb_size);
375   persistent_bootconfig.read(&bootconfig[bootconfig_size + vb_size], pb_size);
376   // Trim the block size padding from the persistent bootconfig
377   bootconfig.erase(bootconfig.find_last_not_of('\0'));
378 
379   // Write out the ramdisks and bootconfig blocks
380   final_rd << boot_ramdisk.rdbuf() << vendor_boot_ramdisk.rdbuf()
381            << bootconfig;
382 
383   // Append bootconfig length
384   bootconfig_size = bootconfig.size();
385   final_rd.write(reinterpret_cast<const char *>(&bootconfig_size),
386                  sizeof(uint32_t));
387 
388   // Append bootconfig checksum
389   uint32_t bootconfig_csum = 0;
390   for (auto i = 0; i < bootconfig_size; i++) {
391     bootconfig_csum += bootconfig[i];
392   }
393   final_rd.write(reinterpret_cast<const char *>(&bootconfig_csum),
394                  sizeof(uint32_t));
395 
396   // Append bootconfig trailer
397   final_rd << "#BOOTCONFIG\n";
398   final_rd.close();
399 }
400 } // namespace cuttlefish
401