• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/assemble_cvd/boot_config.h"
18 
19 #include <fstream>
20 #include <sstream>
21 #include <string>
22 
23 #include <sys/stat.h>
24 
25 #include <android-base/logging.h>
26 #include <android-base/strings.h>
27 #include <gflags/gflags.h>
28 
29 #include "common/libs/utils/environment.h"
30 #include "common/libs/utils/files.h"
31 #include "common/libs/utils/size_utils.h"
32 #include "common/libs/utils/subprocess.h"
33 #include "host/libs/config/bootconfig_args.h"
34 #include "host/libs/config/cuttlefish_config.h"
35 #include "host/libs/config/kernel_args.h"
36 #include "host/libs/vm_manager/crosvm_manager.h"
37 #include "host/libs/vm_manager/vm_manager.h"
38 
39 using cuttlefish::vm_manager::CrosvmManager;
40 
41 DECLARE_bool(pause_in_bootloader);
42 DECLARE_string(vm_manager);
43 
44 // Taken from external/avb/avbtool.py; this define is not in the headers
45 #define MAX_AVB_METADATA_SIZE 69632ul
46 
47 namespace cuttlefish {
48 namespace {
49 
WriteEnvironment(const CuttlefishConfig & config,const std::string & kernel_args,const std::string & env_path)50 size_t WriteEnvironment(const CuttlefishConfig& config,
51                         const std::string& kernel_args,
52                         const std::string& env_path) {
53   std::ostringstream env;
54 
55   if (!kernel_args.empty()) {
56     env << "uenvcmd=setenv bootargs \"$cbootargs " << kernel_args << "\" && ";
57   } else {
58     env << "uenvcmd=setenv bootargs \"$cbootargs\" && ";
59   }
60   if (FLAGS_pause_in_bootloader) {
61     env << "if test $paused -ne 1; then paused=1; else run bootcmd_android; fi";
62   } else {
63     env << "run bootcmd_android";
64   }
65   env << '\0';
66   if (!config.boot_slot().empty()) {
67     env << "android_slot_suffix=_" << config.boot_slot() << '\0';
68   }
69   env << '\0';
70 
71   std::string env_str = env.str();
72   std::ofstream file_out(env_path.c_str(), std::ios::binary);
73   file_out << env_str;
74 
75   if (!file_out.good()) {
76     return 0;
77   }
78 
79   return env_str.length();
80 }
81 
82 }  // namespace
83 
84 class InitBootloaderEnvPartitionImpl : public InitBootloaderEnvPartition {
85  public:
INJECT(InitBootloaderEnvPartitionImpl (const CuttlefishConfig & config,const CuttlefishConfig::InstanceSpecific & instance))86   INJECT(InitBootloaderEnvPartitionImpl(
87       const CuttlefishConfig& config,
88       const CuttlefishConfig::InstanceSpecific& instance))
89       : config_(config), instance_(instance) {}
90 
91   // SetupFeature
Name() const92   std::string Name() const override { return "InitBootloaderEnvPartitionImpl"; }
Enabled() const93   bool Enabled() const override { return !config_.protected_vm(); }
94 
95  private:
Dependencies() const96   std::unordered_set<SetupFeature*> Dependencies() const override { return {}; }
Setup()97   bool Setup() override {
98     auto boot_env_image_path = instance_.uboot_env_image_path();
99     auto tmp_boot_env_image_path = boot_env_image_path + ".tmp";
100     auto uboot_env_path = instance_.PerInstancePath("mkenvimg_input");
101     auto kernel_cmdline =
102         android::base::Join(KernelCommandLineFromConfig(config_), " ");
103     // If the bootconfig isn't supported in the guest kernel, the bootconfig
104     // args need to be passed in via the uboot env. This won't be an issue for
105     // protect kvm which is running a kernel with bootconfig support.
106     if (!config_.bootconfig_supported()) {
107       auto bootconfig_args = android::base::Join(
108           BootconfigArgsFromConfig(config_, instance_), " ");
109       // "androidboot.hardware" kernel parameter has changed to "hardware" in
110       // bootconfig and needs to be replaced before being used in the kernel
111       // cmdline.
112       bootconfig_args = android::base::StringReplace(
113           bootconfig_args, " hardware=", " androidboot.hardware=", true);
114       // TODO(b/182417593): Until we pass the module parameters through
115       // modules.options, we pass them through bootconfig using
116       // 'kernel.<key>=<value>' But if we don't support bootconfig, we need to
117       // rename them back to the old cmdline version
118       bootconfig_args =
119           android::base::StringReplace(bootconfig_args, " kernel.", " ", true);
120       kernel_cmdline += " ";
121       kernel_cmdline += bootconfig_args;
122     }
123     if (!WriteEnvironment(config_, kernel_cmdline, uboot_env_path)) {
124       LOG(ERROR) << "Unable to write out plaintext env '" << uboot_env_path
125                  << ".'";
126       return false;
127     }
128 
129     auto mkimage_path = HostBinaryPath("mkenvimage_slim");
130     Command cmd(mkimage_path);
131     cmd.AddParameter("-output_path");
132     cmd.AddParameter(tmp_boot_env_image_path);
133     cmd.AddParameter("-input_path");
134     cmd.AddParameter(uboot_env_path);
135     int success = cmd.Start().Wait();
136     if (success != 0) {
137       LOG(ERROR) << "Unable to run mkenvimage_slim. Exited with status "
138                  << success;
139       return false;
140     }
141 
142     const off_t boot_env_size_bytes = AlignToPowerOf2(
143         MAX_AVB_METADATA_SIZE + 4096, PARTITION_SIZE_SHIFT);
144 
145     auto avbtool_path = HostBinaryPath("avbtool");
146     Command boot_env_hash_footer_cmd(avbtool_path);
147     boot_env_hash_footer_cmd.AddParameter("add_hash_footer");
148     boot_env_hash_footer_cmd.AddParameter("--image");
149     boot_env_hash_footer_cmd.AddParameter(tmp_boot_env_image_path);
150     boot_env_hash_footer_cmd.AddParameter("--partition_size");
151     boot_env_hash_footer_cmd.AddParameter(boot_env_size_bytes);
152     boot_env_hash_footer_cmd.AddParameter("--partition_name");
153     boot_env_hash_footer_cmd.AddParameter("uboot_env");
154     boot_env_hash_footer_cmd.AddParameter("--key");
155     boot_env_hash_footer_cmd.AddParameter(
156         DefaultHostArtifactsPath("etc/cvd_avb_testkey.pem"));
157     boot_env_hash_footer_cmd.AddParameter("--algorithm");
158     boot_env_hash_footer_cmd.AddParameter("SHA256_RSA4096");
159     success = boot_env_hash_footer_cmd.Start().Wait();
160     if (success != 0) {
161       LOG(ERROR) << "Unable to run append hash footer. Exited with status "
162                  << success;
163       return false;
164     }
165 
166     if (!FileExists(boot_env_image_path) ||
167         ReadFile(boot_env_image_path) != ReadFile(tmp_boot_env_image_path)) {
168       if (!RenameFile(tmp_boot_env_image_path, boot_env_image_path)) {
169         LOG(ERROR) << "Unable to delete the old env image.";
170         return false;
171       }
172       LOG(DEBUG) << "Updated bootloader environment image.";
173     } else {
174       RemoveFile(tmp_boot_env_image_path);
175     }
176 
177     return true;
178   }
179 
180   const CuttlefishConfig& config_;
181   const CuttlefishConfig::InstanceSpecific& instance_;
182 };
183 
184 fruit::Component<fruit::Required<const CuttlefishConfig,
185                                  const CuttlefishConfig::InstanceSpecific>,
186                  InitBootloaderEnvPartition>
InitBootloaderEnvPartitionComponent()187 InitBootloaderEnvPartitionComponent() {
188   return fruit::createComponent()
189       .bind<InitBootloaderEnvPartition, InitBootloaderEnvPartitionImpl>()
190       .addMultibinding<SetupFeature, InitBootloaderEnvPartition>();
191 }
192 
193 } // namespace cuttlefish
194