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