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/libs/vm_manager/vm_manager.h"
18
19 #include <memory>
20
21 #include <android-base/logging.h>
22 #include <sys/utsname.h>
23
24 #include "common/libs/utils/users.h"
25 #include "host/libs/config/cuttlefish_config.h"
26 #include "host/libs/vm_manager/qemu_manager.h"
27 #include "host/libs/vm_manager/crosvm_manager.h"
28
29 namespace cuttlefish {
30 namespace vm_manager {
31
VmManager(const cuttlefish::CuttlefishConfig * config)32 VmManager::VmManager(const cuttlefish::CuttlefishConfig* config)
33 : config_(config) {}
34
35 namespace{
36 template <typename T>
GetManagerSingleton(const cuttlefish::CuttlefishConfig * config)37 VmManager* GetManagerSingleton(const cuttlefish::CuttlefishConfig* config) {
38 static std::shared_ptr<VmManager> vm_manager(new T(config));
39 return vm_manager.get();
40 }
41 } // namespace
42
43 std::map<std::string, VmManager::VmManagerHelper>
44 VmManager::vm_manager_helpers_ = {
45 {
46 QemuManager::name(),
47 {
48 GetManagerSingleton<QemuManager>,
49 cuttlefish::HostSupportsQemuCli,
50 QemuManager::ConfigureGpu,
51 QemuManager::ConfigureBootDevices,
52 },
53 },
54 {
55 CrosvmManager::name(),
56 {
57 GetManagerSingleton<CrosvmManager>,
58 // Same as Qemu for the time being
59 cuttlefish::HostSupportsQemuCli,
60 CrosvmManager::ConfigureGpu,
61 CrosvmManager::ConfigureBootDevices,
62 }
63 }
64 };
65
Get(const std::string & vm_manager_name,const cuttlefish::CuttlefishConfig * config)66 VmManager* VmManager::Get(const std::string& vm_manager_name,
67 const cuttlefish::CuttlefishConfig* config) {
68 if (VmManager::IsValidName(vm_manager_name)) {
69 return vm_manager_helpers_[vm_manager_name].builder(config);
70 }
71 LOG(ERROR) << "Requested invalid VmManager: " << vm_manager_name;
72 return nullptr;
73 }
74
IsValidName(const std::string & name)75 bool VmManager::IsValidName(const std::string& name) {
76 return vm_manager_helpers_.count(name) > 0;
77 }
78
IsVmManagerSupported(const std::string & name)79 bool VmManager::IsVmManagerSupported(const std::string& name) {
80 return VmManager::IsValidName(name) &&
81 vm_manager_helpers_[name].support_checker();
82 }
83
ConfigureGpuMode(const std::string & vmm_name,const std::string & gpu_mode)84 std::vector<std::string> VmManager::ConfigureGpuMode(
85 const std::string& vmm_name, const std::string& gpu_mode) {
86 auto it = vm_manager_helpers_.find(vmm_name);
87 if (it == vm_manager_helpers_.end()) {
88 return {};
89 }
90 return it->second.configure_gpu_mode(gpu_mode);
91 }
92
ConfigureBootDevices(const std::string & vmm_name)93 std::vector<std::string> VmManager::ConfigureBootDevices(
94 const std::string& vmm_name) {
95 auto it = vm_manager_helpers_.find(vmm_name);
96 if (it == vm_manager_helpers_.end()) {
97 return {};
98 }
99 return it->second.configure_boot_devices();
100 }
101
GetValidNames()102 std::vector<std::string> VmManager::GetValidNames() {
103 std::vector<std::string> ret = {};
104 for (const auto& key_val: vm_manager_helpers_) {
105 ret.push_back(key_val.first);
106 }
107 return ret;
108 }
109
UserInGroup(const std::string & group,std::vector<std::string> * config_commands)110 bool VmManager::UserInGroup(const std::string& group,
111 std::vector<std::string>* config_commands) {
112 if (!cuttlefish::InGroup(group)) {
113 LOG(ERROR) << "User must be a member of " << group;
114 config_commands->push_back("# Add your user to the " + group + " group:");
115 config_commands->push_back("sudo usermod -aG " + group + " $USER");
116 return false;
117 }
118 return true;
119 }
120
GetLinuxVersion()121 std::pair<int,int> VmManager::GetLinuxVersion() {
122 struct utsname info;
123 if (!uname(&info)) {
124 char* digit = strtok(info.release, "+.-");
125 int major = atoi(digit);
126 if (digit) {
127 digit = strtok(NULL, "+.-");
128 int minor = atoi(digit);
129 return std::pair<int,int>{major, minor};
130 }
131 }
132 LOG(ERROR) << "Failed to detect Linux kernel version";
133 return invalid_linux_version;
134 }
135
LinuxVersionAtLeast(std::vector<std::string> * config_commands,const std::pair<int,int> & version,int major,int minor)136 bool VmManager::LinuxVersionAtLeast(std::vector<std::string>* config_commands,
137 const std::pair<int,int>& version,
138 int major, int minor) {
139 if (version.first > major ||
140 (version.first == major && version.second >= minor)) {
141 return true;
142 }
143
144 LOG(ERROR) << "Kernel version must be >=" << major << "." << minor
145 << ", have " << version.first << "." << version.second;
146 config_commands->push_back("# Please upgrade your kernel to >=" +
147 std::to_string(major) + "." +
148 std::to_string(minor));
149 return false;
150 }
151
ValidateHostConfiguration(std::vector<std::string> * config_commands) const152 bool VmManager::ValidateHostConfiguration(
153 std::vector<std::string>* config_commands) const {
154 // if we can't detect the kernel version, just fail
155 auto version = VmManager::GetLinuxVersion();
156 if (version == invalid_linux_version) {
157 return false;
158 }
159
160 // the check for cvdnetwork needs to happen even if the user is not in kvm, so
161 // we can't just say UserInGroup("kvm") && UserInGroup("cvdnetwork")
162 auto in_cvdnetwork = VmManager::UserInGroup("cvdnetwork", config_commands);
163
164 // if we're in the virtaccess group this is likely to be a CrOS environment.
165 auto is_cros = cuttlefish::InGroup("virtaccess");
166 if (is_cros) {
167 // relax the minimum kernel requirement slightly, as chromeos-4.4 has the
168 // needed backports to enable vhost_vsock
169 auto linux_ver_4_4 =
170 VmManager::LinuxVersionAtLeast(config_commands, version, 4, 4);
171 return in_cvdnetwork && linux_ver_4_4;
172 } else {
173 // this is regular Linux, so use the Debian group name and be more
174 // conservative with the kernel version check.
175 auto in_kvm = VmManager::UserInGroup("kvm", config_commands);
176 auto linux_ver_4_8 =
177 VmManager::LinuxVersionAtLeast(config_commands, version, 4, 8);
178 return in_cvdnetwork && in_kvm && linux_ver_4_8;
179 }
180 }
181
WithFrontend(bool enabled)182 void VmManager::WithFrontend(bool enabled) {
183 frontend_enabled_ = enabled;
184 }
185
WithKernelCommandLine(const std::string & kernel_cmdline)186 void VmManager::WithKernelCommandLine(const std::string& kernel_cmdline) {
187 kernel_cmdline_ = kernel_cmdline;
188 }
189
190 } // namespace vm_manager
191 } // namespace cuttlefish
192
193