Home | History | Annotate | Download | only in emulation
      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