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/result.h"
32 #include "common/libs/utils/size_utils.h"
33 #include "common/libs/utils/subprocess.h"
34 #include "host/commands/assemble_cvd/bootconfig_args.h"
35 #include "host/libs/avb/avb.h"
36 #include "host/libs/config/cuttlefish_config.h"
37 #include "host/libs/config/kernel_args.h"
38 #include "host/libs/vm_manager/crosvm_manager.h"
39 #include "host/libs/vm_manager/vm_manager.h"
40 
41 using cuttlefish::vm_manager::CrosvmManager;
42 
43 DECLARE_string(vm_manager);
44 
45 namespace cuttlefish {
46 namespace {
47 
48 // The ordering of tap devices we're passing to crosvm / qemu is important
49 // Ethernet tap device is the second one (eth1) we're passing ATM
50 static constexpr char kUbootPrimaryEth[] = "eth1";
51 
WritePausedEntrypoint(const std::string & entrypoint,const CuttlefishConfig::InstanceSpecific & instance,std::ostream & env)52 void WritePausedEntrypoint(const std::string& entrypoint,
53                            const CuttlefishConfig::InstanceSpecific& instance,
54                            std::ostream& env) {
55   if (instance.pause_in_bootloader()) {
56     env << "if test $paused -ne 1; then paused=1; else " << entrypoint << "; fi";
57   } else {
58     env << entrypoint;
59   }
60 
61   env << '\0';
62 }
63 
WriteAndroidEnvironment(std::ostream & env,const CuttlefishConfig::InstanceSpecific & instance)64 void WriteAndroidEnvironment(
65     std::ostream& env,
66     const CuttlefishConfig::InstanceSpecific& instance) {
67   WritePausedEntrypoint("run bootcmd_android", instance, env);
68 
69   if (!instance.boot_slot().empty()) {
70     env << "android_slot_suffix=_" << instance.boot_slot() << '\0';
71   }
72   env << '\0';
73 }
74 
WriteEFIEnvironment(const CuttlefishConfig::InstanceSpecific & instance,std::optional<std::uint16_t> partition_num,std::ostream & env)75 void WriteEFIEnvironment(const CuttlefishConfig::InstanceSpecific& instance,
76                          std::optional<std::uint16_t> partition_num,
77                          std::ostream& env) {
78   std::string partition_str =
79       partition_num ? fmt::format("setenv devplist {:x};", *partition_num) : "";
80   WritePausedEntrypoint(
81       partition_str +
82           "load virtio 0:${devplist} ${loadaddr} efi/boot/bootaa64.efi "
83           "&& bootefi ${loadaddr} ${fdtcontroladdr}; "
84           "load virtio 0:${devplist} ${loadaddr} efi/boot/bootia32.efi && "
85           "bootefi ${loadaddr} ${fdtcontroladdr};"
86           "load virtio 0:${devplist} ${loadaddr} efi/boot/bootriscv64.efi && "
87           "bootefi ${loadaddr} ${fdtcontroladdr}",
88       instance, env);
89 }
90 
WriteEnvironment(const CuttlefishConfig::InstanceSpecific & instance,const CuttlefishConfig::InstanceSpecific::BootFlow & flow,const std::string & kernel_args,const std::string & env_path)91 size_t WriteEnvironment(const CuttlefishConfig::InstanceSpecific& instance,
92                         const CuttlefishConfig::InstanceSpecific::BootFlow& flow,
93                         const std::string& kernel_args,
94                         const std::string& env_path) {
95   std::ostringstream env;
96 
97   env << "ethprime=" << kUbootPrimaryEth << '\0';
98   if (!kernel_args.empty()) {
99     env << "uenvcmd=setenv bootargs \"$cbootargs " << kernel_args << "\" && ";
100   } else {
101     env << "uenvcmd=setenv bootargs \"$cbootargs\" && ";
102   }
103 
104   switch (flow) {
105     case CuttlefishConfig::InstanceSpecific::BootFlow::Android:
106       WriteAndroidEnvironment(env, instance);
107       break;
108     case CuttlefishConfig::InstanceSpecific::BootFlow::AndroidEfiLoader:
109       WriteEFIEnvironment(instance, 1, env);
110       break;
111     case CuttlefishConfig::InstanceSpecific::BootFlow::ChromeOs:
112       WriteEFIEnvironment(instance, 2, env);
113       break;
114     case CuttlefishConfig::InstanceSpecific::BootFlow::ChromeOsDisk:
115       WriteEFIEnvironment(instance, 12, env);
116       break;
117     case CuttlefishConfig::InstanceSpecific::BootFlow::Fuchsia:
118     case CuttlefishConfig::InstanceSpecific::BootFlow::Linux:
119       WriteEFIEnvironment(instance, {}, env);
120       break;
121   }
122 
123   std::string env_str = env.str();
124   std::ofstream file_out(env_path.c_str(), std::ios::binary);
125   file_out << env_str;
126 
127   if (!file_out.good()) {
128     return 0;
129   }
130 
131   return env_str.length();
132 }
133 
ReplaceKernelBootArgs(const std::unordered_map<std::string,std::string> & args)134 std::unordered_map<std::string, std::string> ReplaceKernelBootArgs(
135     const std::unordered_map<std::string, std::string>& args) {
136   std::unordered_map<std::string, std::string> ret;
137   std::transform(std::begin(args), std::end(args),
138                  std::inserter(ret, ret.end()), [](const auto& kv) {
139                    const auto& k = kv.first;
140                    const auto& v = kv.second;
141                    return std::make_pair(
142                        android::base::StringReplace(k, " kernel.", " ", true),
143                        v);
144                  });
145   return ret;
146 }
147 
PrepareBootEnvImage(const CuttlefishConfig & config,const CuttlefishConfig::InstanceSpecific & instance,const std::string & image_path,const CuttlefishConfig::InstanceSpecific::BootFlow & flow)148 Result<void> PrepareBootEnvImage(
149     const CuttlefishConfig& config,
150     const CuttlefishConfig::InstanceSpecific& instance,
151     const std::string& image_path,
152     const CuttlefishConfig::InstanceSpecific::BootFlow& flow) {
153   if (instance.protected_vm()) {
154     return {};
155   }
156   auto tmp_boot_env_image_path = image_path + ".tmp";
157   auto uboot_env_path = instance.PerInstancePath("mkenvimg_input");
158   auto kernel_cmdline =
159       android::base::Join(KernelCommandLineFromConfig(config, instance), " ");
160   // If the bootconfig isn't supported in the guest kernel, the bootconfig
161   // args need to be passed in via the uboot env. This won't be an issue for
162   // protect kvm which is running a kernel with bootconfig support.
163   if (!instance.bootconfig_supported()) {
164     auto bootconfig_args =
165         CF_EXPECT(BootconfigArgsFromConfig(config, instance));
166 
167     // "androidboot.hardware" kernel parameter has changed to "hardware" in
168     // bootconfig and needs to be replaced before being used in the kernel
169     // cmdline.
170     auto bootconfig_hardware_it = bootconfig_args.find("hardware");
171     if (bootconfig_hardware_it != bootconfig_args.end()) {
172       bootconfig_args["androidboot.hardware"] = bootconfig_hardware_it->second;
173       bootconfig_args.erase(bootconfig_hardware_it);
174     }
175 
176     // TODO(b/182417593): Until we pass the module parameters through
177     // modules.options, we pass them through bootconfig using
178     // 'kernel.<key>=<value>' But if we don't support bootconfig, we need to
179     // rename them back to the old cmdline version
180     bootconfig_args = ReplaceKernelBootArgs(bootconfig_args);
181 
182     kernel_cmdline +=
183         " " + CF_EXPECT(BootconfigArgsString(bootconfig_args, " "));
184   }
185 
186   CF_EXPECTF(WriteEnvironment(instance, flow, kernel_cmdline, uboot_env_path),
187              "Unable to write out plaintext env '{}'", uboot_env_path);
188 
189   auto mkimage_path = HostBinaryPath("mkenvimage_slim");
190   Command cmd(mkimage_path);
191   cmd.AddParameter("-output_path");
192   cmd.AddParameter(tmp_boot_env_image_path);
193   cmd.AddParameter("-input_path");
194   cmd.AddParameter(uboot_env_path);
195   int success = cmd.Start().Wait();
196   CF_EXPECTF(success == 0,
197              "Unable to run mkenvimage_slim. Exited with status {}", success);
198 
199   const off_t boot_env_size_bytes =
200       AlignToPowerOf2(kMaxAvbMetadataSize + 4096, PARTITION_SIZE_SHIFT);
201 
202   std::unique_ptr<Avb> avbtool = GetDefaultAvb();
203   CF_EXPECT(avbtool->AddHashFooter(tmp_boot_env_image_path, "uboot_env",
204                                    boot_env_size_bytes));
205 
206   if (!FileExists(image_path) ||
207       ReadFile(image_path) != ReadFile(tmp_boot_env_image_path)) {
208     CF_EXPECT(RenameFile(tmp_boot_env_image_path, image_path),
209               "Unable to delete the old env image");
210     LOG(DEBUG) << "Updated bootloader environment image.";
211   } else {
212     RemoveFile(tmp_boot_env_image_path);
213   }
214 
215   return {};
216 }
217 
218 }  // namespace
219 
InitBootloaderEnvPartition(const CuttlefishConfig & config,const CuttlefishConfig::InstanceSpecific & instance)220 Result<void> InitBootloaderEnvPartition(
221     const CuttlefishConfig& config,
222     const CuttlefishConfig::InstanceSpecific& instance) {
223   if (instance.ap_boot_flow() ==
224       CuttlefishConfig::InstanceSpecific::APBootFlow::Grub) {
225     CF_EXPECT(PrepareBootEnvImage(
226         config, instance, instance.ap_uboot_env_image_path(),
227         CuttlefishConfig::InstanceSpecific::BootFlow::Linux));
228   }
229   CF_EXPECT(PrepareBootEnvImage(
230       config, instance, instance.uboot_env_image_path(), instance.boot_flow()));
231   return {};
232 }
233 
234 } // namespace cuttlefish
235