Check HAXM capability in QEMU

Checking HAXM capability to check if emulator can use HAXM virtalization.
Currently two possibility that HAXM not work. Firstly is VT/NX is not enabled
in the system, seconly is HAXM have used up the memory quota. See code comemnts
for the memry quota explaination.

Checking the capability in advance can give user more information that why HAXM
is not working and be more user friendly.

Change-Id: I13f73734cf783398f485fabd19cce43364b571c3
Signed-off-by: Xin, Xiaohui <xiaohui.xin@intel.com>
Signed-off-by: Gao, Fengqian <fengqian.gao@intel.com>
Signed-off-by: Jiang, Yunhong <yunhong.jiang@intel.com>
diff --git a/hax.h b/hax.h
index 9af61ce..a29e45e 100644
--- a/hax.h
+++ b/hax.h
@@ -11,6 +11,7 @@
 
 #ifdef CONFIG_HAX
 int hax_enabled(void);
+int hax_set_ramsize(uint64_t ramsize);
 int hax_init(int smp_cpus);
 int hax_init_vcpu(CPUState *env);
 /* Execute vcpu in non-root mode */
diff --git a/target-i386/hax-all.c b/target-i386/hax-all.c
index 18d65d0..ac919c5 100644
--- a/target-i386/hax-all.c
+++ b/target-i386/hax-all.c
@@ -124,6 +124,37 @@
 /* Least HAX kernel version */
 uint32_t hax_lest_version = 0x1;
 
+static int hax_get_capability(struct hax_state *hax)
+{
+    int ret;
+    struct hax_capabilityinfo capinfo, *cap = &capinfo;
+
+    ret = hax_capability(hax, cap);
+    if (ret)
+        return -ENOSYS;
+
+    if ( ((cap->wstatus & HAX_CAP_WORKSTATUS_MASK) ==
+          HAX_CAP_STATUS_NOTWORKING ))
+    {
+        if (cap->winfo & HAX_CAP_FAILREASON_VT)
+            dprint("VT feature is not enabled, HAXM not working.\n");
+        else if (cap->winfo & HAX_CAP_FAILREASON_NX)
+            dprint("NX feature is not enabled, HAXM not working.\n");
+        return -ENXIO;
+    }
+
+    if (cap->wstatus & HAX_CAP_MEMQUOTA)
+    {
+        if (cap->mem_quota < hax->mem_quota)
+        {
+            dprint("The memory needed by this VM exceeds the driver limit.\n");
+            return -ENOSPC;
+        }
+    }
+
+    return 0;
+}
+
 static int hax_version_support(struct hax_state *hax)
 {
     int ret;
@@ -297,6 +328,16 @@
     return 0;
 }
 
+int hax_set_ramsize(uint64_t ramsize)
+{
+    struct hax_state *hax = &hax_global;
+
+    memset(hax, 0, sizeof(struct hax_state));
+    hax->mem_quota = ram_size;
+
+    return 0;
+}
+
 int hax_init(int smp_cpus)
 {
     struct hax_state *hax = NULL;
@@ -305,7 +346,6 @@
     hax_support = 0;
 
     hax = &hax_global;
-    memset(hax, 0, sizeof(struct hax_state));
 
     hax->fd = hax_mod_open();
     if (hax_invalid_fd(hax->fd))
@@ -315,6 +355,14 @@
         goto error;
     }
 
+    ret = hax_get_capability(hax);
+    /* In case HAXM have no such capability support */
+    if (ret && (ret != -ENOSYS))
+    {
+        ret = -EINVAL;
+        goto error;
+    }
+
     if (!hax_version_support(hax))
     {
         dprint("Incompatible HAX version. Qemu current version %x ", hax_cur_version );
diff --git a/target-i386/hax-darwin.c b/target-i386/hax-darwin.c
index b6d27c3..2b460ce 100644
--- a/target-i386/hax-darwin.c
+++ b/target-i386/hax-darwin.c
@@ -85,6 +85,20 @@
     return ret;
 }
 
+int hax_capability(struct hax_state *hax, struct hax_capabilityinfo *cap)
+{
+    int ret;
+
+    ret = ioctl(hax->fd, HAX_IOCTL_CAPABILITY, cap);
+    if (ret == -1)
+    {
+        dprint("Failed to get HAX capability\n");
+        return -errno;
+    }
+
+    return 0;
+}
+
 int hax_mod_version(struct hax_state *hax, struct hax_module_version *version)
 {
     int ret;
diff --git a/target-i386/hax-darwin.h b/target-i386/hax-darwin.h
index 261cfd3..2c6129b 100644
--- a/target-i386/hax-darwin.h
+++ b/target-i386/hax-darwin.h
@@ -40,6 +40,8 @@
 #define HAX_IOCTL_VERSION _IOWR(0, 0x20, struct hax_module_version)
 /* Create VM instance and return the vm_id */
 #define HAX_IOCTL_CREATE_VM _IOWR(0, 0x21, int)
+/* Get HAXM capability information */
+#define HAX_IOCTL_CAPABILITY _IOR(0, 0x23, struct hax_capabilityinfo)
 
 /* Pass down a VM_ID, create a VCPU instance for it */
 #define HAX_VM_IOCTL_VCPU_CREATE    _IOR(0, 0x80, int)
diff --git a/target-i386/hax-i386.h b/target-i386/hax-i386.h
index e55e584..27203cf 100644
--- a/target-i386/hax-i386.h
+++ b/target-i386/hax-i386.h
@@ -40,6 +40,7 @@
     hax_fd fd; /* the global hax device interface */
     uint32_t version;
     struct hax_vm *vm;
+    uint64_t mem_quota;
 };
 
 #define HAX_MAX_VCPU 0x10
@@ -67,6 +68,7 @@
 int hax_sync_msr(CPUState *env, struct hax_msr_data *msrs, int set);
 int hax_sync_fpu(CPUState *env, struct fx_layout *fl, int set);
 int hax_vm_destroy(struct hax_vm *vm);
+int hax_capability(struct hax_state *hax, struct hax_capabilityinfo *cap);
 
 /* Common host function */
 int hax_host_create_vm(struct hax_state *hax, int vm_id);
diff --git a/target-i386/hax-interface.h b/target-i386/hax-interface.h
index 5a9ed31..569eeba 100644
--- a/target-i386/hax-interface.h
+++ b/target-i386/hax-interface.h
@@ -347,4 +347,39 @@
     uint64_t va;
 };
 
+/*
+ * We need to load the HAXM (HAX Manager) to tell if the host system has the
+ * required capabilities to operate, and we use hax_capabilityinfo to get such
+ * info from HAXM.
+ *
+ * To prevent HAXM from over-consuming RAM, we set the maximum amount of RAM
+ * that can be used for guests at HAX installation time. Once the quota is
+ * reached, HAXM will no longer attempt to allocate memory for guests.
+ * Detect that HAXM is out of quota can take the emulator to non-HAXM model
+ */
+struct hax_capabilityinfo
+{
+    /* bit 0: 1 - HAXM is working
+     *        0 - HAXM is not working possibly because VT/NX is disabled
+                  NX means Non-eXecution, aks. XD (eXecution Disable)
+     * bit 1: 1 - HAXM has hard limit on how many RAM can be used as guest RAM
+     *        0 - HAXM has no memory limitation
+     */
+#define HAX_CAP_STATUS_WORKING  0x1
+#define HAX_CAP_STATUS_NOTWORKING  0x0
+#define HAX_CAP_WORKSTATUS_MASK 0x1
+#define HAX_CAP_MEMQUOTA        0x2
+    uint16_t wstatus;
+    /*
+     * valid when HAXM is not working
+     * bit 0: HAXM is not working because VT is not enabeld
+     * bit 1: HAXM is not working because NX not enabled
+     */
+#define HAX_CAP_FAILREASON_VT   0x1
+#define HAX_CAP_FAILREASON_NX   0x2
+    uint16_t winfo;
+    uint32_t pad;
+    uint64_t mem_quota;
+};
+
 #endif
diff --git a/target-i386/hax-windows.c b/target-i386/hax-windows.c
index 683a227..346e5c0 100644
--- a/target-i386/hax-windows.c
+++ b/target-i386/hax-windows.c
@@ -133,6 +133,36 @@
         return 0;
 }
 
+int hax_capability(struct hax_state *hax, struct hax_capabilityinfo *cap)
+{
+    int ret;
+    HANDLE hDevice = hax->fd;   //handle to hax module
+    DWORD dSize = 0;
+    DWORD err = 0;
+
+    if (hax_invalid_fd(hDevice)) {
+        dprint("Invalid fd for hax device!\n");
+        return -ENODEV;
+    }
+
+    ret = DeviceIoControl(hDevice,
+      HAX_IOCTL_CAPABILITY,
+      NULL, 0,
+      cap, sizeof(*cap),
+      &dSize,
+      (LPOVERLAPPED) NULL);
+
+    if (!ret) {
+        err = GetLastError();
+        if (err == ERROR_INSUFFICIENT_BUFFER ||
+            err == ERROR_MORE_DATA)
+            dprint("hax capability is too long to hold.\n");
+        dprint("Failed to get Hax capability:%d\n", err);
+        return -EFAULT;
+    } else
+        return 0;
+}
+
 int hax_mod_version(struct hax_state *hax, struct hax_module_version *version)
 {
     int ret;
diff --git a/target-i386/hax-windows.h b/target-i386/hax-windows.h
index b6d60b7..9e596d3 100644
--- a/target-i386/hax-windows.h
+++ b/target-i386/hax-windows.h
@@ -45,6 +45,7 @@
 /* See comments for the ioctl in hax-darwin.h */
 #define HAX_IOCTL_VERSION       CTL_CODE(HAX_DEVICE_TYPE, 0x900, METHOD_BUFFERED, FILE_ANY_ACCESS)
 #define HAX_IOCTL_CREATE_VM     CTL_CODE(HAX_DEVICE_TYPE, 0x901, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define HAX_IOCTL_CAPABILITY    CTL_CODE(HAX_DEVICE_TYPE, 0x910, METHOD_BUFFERED, FILE_ANY_ACCESS)
 
 #define HAX_VM_IOCTL_VCPU_CREATE   CTL_CODE(HAX_DEVICE_TYPE, 0x902, METHOD_BUFFERED, FILE_ANY_ACCESS)
 #define HAX_VM_IOCTL_ALLOC_RAM     CTL_CODE(HAX_DEVICE_TYPE, 0x903, METHOD_BUFFERED, FILE_ANY_ACCESS)
diff --git a/vl-android.c b/vl-android.c
index 8071052..46f869a 100644
--- a/vl-android.c
+++ b/vl-android.c
@@ -4143,6 +4143,7 @@
     {
         int ret;
 
+        hax_set_ramsize(ram_size);
         ret = hax_init(smp_cpus);
         fprintf(stderr, "HAX is %s and emulator runs in %s mode\n",
             !ret ? "working" :"not working", !ret ? "fast virt" : "emulation");