1 // Copyright (C) 2014 The Android Open Source Project 2 // 3 // This software is licensed under the terms of the GNU General Public 4 // License version 2, as published by the Free Software Foundation, and 5 // may be copied, distributed, and modified under those terms. 6 // 7 // This program is distributed in the hope that it will be useful, 8 // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 // GNU General Public License for more details. 11 12 #include "android/emulation/CpuAccelerator.h" 13 14 #ifdef _WIN32 15 #define WIN32_LEAN_AND_MEAN 1 16 #include <windows.h> 17 #include <winioctl.h> 18 #else 19 #include <fcntl.h> 20 #include <string.h> 21 #include <sys/ioctl.h> 22 #include <unistd.h> 23 #endif 24 25 #include <stdio.h> 26 27 #include "android/utils/path.h" 28 29 #include "android/base/Compiler.h" 30 #include "android/base/files/ScopedFd.h" 31 #ifdef _WIN32 32 #include "android/base/files/ScopedHandle.h" 33 #endif 34 #include "android/base/Log.h" 35 #include "android/base/StringFormat.h" 36 37 // NOTE: This source file must be independent of the rest of QEMU, as such 38 // it should not include / reuse any QEMU source file or function 39 // related to KVM or HAX. 40 41 #ifdef __linux__ 42 # define HAVE_KVM 1 43 # define HAVE_HAX 0 44 #elif defined(_WIN32) || defined(__APPLE__) 45 # define HAVE_KVM 0 46 # define HAVE_HAX 1 47 #else 48 # error "Unsupported host platform!" 49 #endif 50 51 namespace android { 52 53 using base::String; 54 using base::StringAppendFormat; 55 using base::ScopedFd; 56 57 namespace { 58 59 struct GlobalState { 60 bool probed; 61 bool testing; 62 CpuAccelerator accel; 63 char status[256]; 64 }; 65 66 GlobalState gGlobals = { false, false, CPU_ACCELERATOR_NONE, { '\0' } }; 67 68 ///////////////////////////////////////////////////////////////////////// 69 ///////////////////////////////////////////////////////////////////////// 70 ///// 71 ///// Linux KVM support. 72 ///// 73 ///////////////////////////////////////////////////////////////////////// 74 ///////////////////////////////////////////////////////////////////////// 75 76 #if HAVE_KVM 77 78 #include <linux/kvm.h> 79 80 // Return true iff KVM is installed and usable on this machine. 81 // |*status| will be set to a small status string explaining the 82 // status of KVM on success or failure. 83 bool ProbeKVM(String *status) { 84 // 1) Check that /dev/kvm exists. 85 if (::access("/dev/kvm", F_OK)) { 86 status->assign( 87 "KVM is not installed on this machine (/dev/kvm is missing)."); 88 return false; 89 } 90 // 2) Check that /dev/kvm can be opened. 91 if (::access("/dev/kvm", R_OK)) { 92 status->assign( 93 "This user doesn't have permissions to use KVM (/dev/kvm)."); 94 return false; 95 } 96 // 3) Open the file. 97 ScopedFd fd(TEMP_FAILURE_RETRY(open("/dev/kvm", O_RDWR))); 98 if (!fd.valid()) { 99 status->assign("Could not open /dev/kvm :"); 100 status->append(strerror(errno)); 101 return false; 102 } 103 104 // 4) Extract KVM version number. 105 int version = ::ioctl(fd.get(), KVM_GET_API_VERSION, 0); 106 if (version < 0) { 107 status->assign("Could not extract KVM version: "); 108 status->append(strerror(errno)); 109 return false; 110 } 111 112 // 5) Compare to minimum supported version 113 status->clear(); 114 115 if (version < KVM_API_VERSION) { 116 StringAppendFormat(status, 117 "KVM version too old: %d (expected at least %d)\n", 118 version, 119 KVM_API_VERSION); 120 return false; 121 } 122 123 // 6) Profit! 124 StringAppendFormat(status, 125 "KVM (version %d) is installed and usable.", 126 version); 127 return true; 128 } 129 130 #endif // HAVE_KVM 131 132 133 #if HAVE_HAX 134 135 // Version numbers for the HAX kernel module. 136 // |compat_version| is the minimum API version supported by the module. 137 // |current_version| is its current API version. 138 struct HaxModuleVersion { 139 uint32_t compat_version; 140 uint32_t current_version; 141 }; 142 143 144 ///////////////////////////////////////////////////////////////////////// 145 ///////////////////////////////////////////////////////////////////////// 146 ///// 147 ///// Windows HAX support. 148 ///// 149 ///////////////////////////////////////////////////////////////////////// 150 ///////////////////////////////////////////////////////////////////////// 151 152 #if defined(_WIN32) 153 154 using base::ScopedHandle; 155 156 // Windows IOCTL code to extract HAX kernel module version. 157 #define HAX_DEVICE_TYPE 0x4000 158 #define HAX_IOCTL_VERSION \ 159 CTL_CODE(HAX_DEVICE_TYPE, 0x900, METHOD_BUFFERED, FILE_ANY_ACCESS) 160 161 // The minimum API version supported by the Android emulator. 162 #define HAX_MIN_VERSION 1 163 164 bool ProbeHAX(String* status) { 165 status->clear(); 166 // 1) Try to find the HAX kernel module. 167 ScopedHandle hax(CreateFile("\\\\.\\HAX", 168 GENERIC_READ | GENERIC_WRITE, 169 0, 170 NULL, 171 CREATE_ALWAYS, 172 FILE_ATTRIBUTE_NORMAL, 173 NULL)); 174 if (!hax.valid()) { 175 DWORD err = GetLastError(); 176 if (err == ERROR_FILE_NOT_FOUND) { 177 status->assign("HAX kernel module is not installed!"); 178 } else { 179 StringAppendFormat(status, 180 "Opening HAX kernel module failed: %u", 181 err); 182 } 183 return false; 184 } 185 186 // 2) Extract the module's version. 187 HaxModuleVersion hax_version; 188 189 DWORD dSize = 0; 190 BOOL ret = DeviceIoControl(hax.get(), 191 HAX_IOCTL_VERSION, 192 NULL, 0, 193 &hax_version, sizeof(hax_version), 194 &dSize, 195 (LPOVERLAPPED) NULL); 196 if (!ret) { 197 DWORD err = GetLastError(); 198 StringAppendFormat(status, 199 "Could not extract HAX module version: %u", 200 err); 201 return false; 202 } 203 204 // 3) Check that it is the right version. 205 if (hax_version.current_version < HAX_MIN_VERSION) { 206 StringAppendFormat(status, 207 "HAX version (%d) is too old (need at least %d).", 208 hax_version.current_version, 209 HAX_MIN_VERSION); 210 return false; 211 } 212 213 // 4) Profit! 214 StringAppendFormat(status, 215 "HAX (version %d) is installed and usable.", 216 hax_version.current_version); 217 return true; 218 } 219 220 #elif defined(__APPLE__) 221 222 ///////////////////////////////////////////////////////////////////////// 223 ///////////////////////////////////////////////////////////////////////// 224 ///// 225 ///// Darwin HAX support. 226 ///// 227 ///////////////////////////////////////////////////////////////////////// 228 ///////////////////////////////////////////////////////////////////////// 229 230 // An IOCTL command number used to retrieve the HAX kernel module version. 231 #define HAX_IOCTL_VERSION _IOWR(0, 0x20, HaxModuleVersion) 232 233 // The minimum API version supported by the Android emulator. 234 #define HAX_MIN_VERSION 1 235 236 bool ProbeHAX(String* status) { 237 // 1) Check that /dev/HAX exists. 238 if (::access("/dev/HAX", F_OK)) { 239 status->assign( 240 "HAX is not installed on this machine (/dev/HAX is missing)."); 241 return false; 242 } 243 // 2) Check that /dev/HAX can be opened. 244 if (::access("/dev/HAX", R_OK)) { 245 status->assign( 246 "This user doesn't have permission to use HAX (/dev/HAX)."); 247 return false; 248 } 249 // 3) Open the file. 250 ScopedFd fd(open("/dev/HAX", O_RDWR)); 251 if (!fd.valid()) { 252 status->assign("Could not open /dev/HAX: "); 253 status->append(strerror(errno)); 254 return false; 255 } 256 257 // 4) Extract HAX version number. 258 status->clear(); 259 260 HaxModuleVersion hax_version; 261 if (::ioctl(fd.get(), HAX_IOCTL_VERSION, &hax_version) < 0) { 262 StringAppendFormat(status, 263 "Could not extract HAX version: %s", 264 strerror(errno)); 265 return false; 266 } 267 268 if (hax_version.current_version < hax_version.compat_version) { 269 StringAppendFormat( 270 status, 271 "Malformed HAX version numbers (current=%d, compat=%d)\n", 272 hax_version.current_version, 273 hax_version.compat_version); 274 return false; 275 } 276 277 // 5) Compare to minimum supported version. 278 279 if (hax_version.current_version < HAX_MIN_VERSION) { 280 StringAppendFormat(status, 281 "HAX version too old: %d (expected at least %d)\n", 282 hax_version.current_version, 283 HAX_MIN_VERSION); 284 return false; 285 } 286 287 // 6) Profit! 288 StringAppendFormat(status, 289 "HAX (version %d) is installed and usable.", 290 hax_version.current_version); 291 return true; 292 } 293 294 #else // !_WIN32 && !__APPLE__ 295 #error "Unsupported HAX host platform" 296 #endif // !_WIN32 && !__APPLE__ 297 298 #endif // HAVE_HAX 299 300 } // namespace 301 302 CpuAccelerator GetCurrentCpuAccelerator() { 303 GlobalState* g = &gGlobals; 304 305 if (g->probed || g->testing) { 306 return g->accel; 307 } 308 309 String status; 310 #if HAVE_KVM 311 if (ProbeKVM(&status)) { 312 g->accel = CPU_ACCELERATOR_KVM; 313 } 314 #elif HAVE_HAX 315 if (ProbeHAX(&status)) { 316 g->accel = CPU_ACCELERATOR_HAX; 317 } 318 #else 319 status = "This system does not support CPU acceleration."; 320 #endif 321 ::snprintf(g->status, sizeof(g->status), "%s", status.c_str()); 322 323 g->probed = true; 324 return g->accel; 325 } 326 327 String GetCurrentCpuAcceleratorStatus() { 328 GlobalState *g = &gGlobals; 329 330 if (!g->probed && !g->testing) { 331 // Force detection of the current CPU accelerator. 332 GetCurrentCpuAccelerator(); 333 } 334 335 return String(g->status); 336 } 337 338 void SetCurrentCpuAcceleratorForTesting(CpuAccelerator accel, 339 const char* status) { 340 GlobalState *g = &gGlobals; 341 342 g->testing = true; 343 g->accel = accel; 344 ::snprintf(g->status, sizeof(g->status), "%s", status); 345 } 346 347 } // namespace android 348