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