Browse Source

implement get capabilities, populate ID registers

Zhuowei Zhang 3 years ago
parent
commit
ab82be558c
2 changed files with 132 additions and 34 deletions
  1. 91 34
      hv.m
  2. 41 0
      hv_kernel_structs.h

+ 91 - 34
hv.m

@@ -1,5 +1,6 @@
 // Decompiled by hand (based-ish on a Ghidra decompile) from Hypervisor.framework on macOS 12.0b1
 // Decompiled by hand (based-ish on a Ghidra decompile) from Hypervisor.framework on macOS 12.0b1
 @import Darwin;
 @import Darwin;
+@import Dispatch;
 #include <Hypervisor/Hypervisor.h>
 #include <Hypervisor/Hypervisor.h>
 #include <assert.h>
 #include <assert.h>
 #include "hv_kernel_structs.h"
 #include "hv_kernel_structs.h"
@@ -38,6 +39,50 @@ static uint64_t hv_trap_wrap(unsigned int hv_call, void* hv_arg) {
 }
 }
 //#define hv_trap hv_trap_wrap
 //#define hv_trap hv_trap_wrap
 
 
+static hv_return_t _hv_get_capabilities(hv_capabilities_t** c) {
+  static dispatch_once_t caps_once;
+  static hv_capabilities_t caps;
+  static hv_return_t status;
+  dispatch_once(&caps_once, ^{
+    status = hv_trap(HV_CALL_VM_GET_CAPABILITIES, &caps);
+  });
+  *c = &caps;
+  return status;
+}
+
+// this is placed at offset 8 of the cpu regs, so I'm labelling the offsets relative to those
+struct hv_vcpu_data_feature_regs {
+  uint64_t aa64dfr0_el1;   // 0x8
+  uint64_t aa64dfr1_el1;   // 0x10
+  uint64_t aa64isar0_el1;  // 0x18
+  uint64_t aa64isar1_el1;  // 0x20
+  uint64_t aa64mmfr0_el1;  // 0x28
+  uint64_t aa64mmfr1_el1;  // 0x30
+  uint64_t aa64mmfr2_el1;  // 0x38
+  uint64_t aa64pfr0_el1;   // 0x40
+  uint64_t aa64pfr1_el1;   // 0x48
+};
+
+static hv_return_t _hv_vcpu_config_get_feature_regs(
+    struct hv_vcpu_data_feature_regs* feature_regs) {
+  hv_capabilities_t* caps = nil;
+  hv_return_t err = _hv_get_capabilities(&caps);
+  if (err) {
+    return err;
+  }
+  // TODO(zhuowei): they do a bunch of fancy shifting to flip a few bits; we just copy over
+  feature_regs->aa64dfr0_el1 = caps->id_aa64dfr0_el1;
+  feature_regs->aa64dfr1_el1 = caps->id_aa64dfr1_el1;
+  feature_regs->aa64isar0_el1 = caps->id_aa64isar0_el1;
+  feature_regs->aa64isar1_el1 = caps->id_aa64isar1_el1;
+  feature_regs->aa64mmfr0_el1 = caps->id_aa64mmfr0_el1;
+  feature_regs->aa64mmfr1_el1 = caps->id_aa64mmfr1_el1;
+  feature_regs->aa64mmfr2_el1 = caps->id_aa64mmfr2_el1;
+  feature_regs->aa64pfr0_el1 = caps->id_aa64pfr0_el1;
+  feature_regs->aa64pfr1_el1 = caps->id_aa64pfr1_el1;
+  return 0;
+}
+
 // type lookup hv_vm_create_t
 // type lookup hv_vm_create_t
 struct hv_vm_create_kernel_args {
 struct hv_vm_create_kernel_args {
   uint64_t min_ipa;
   uint64_t min_ipa;
@@ -101,20 +146,12 @@ struct hv_vcpu_zone {
 static_assert(sizeof(struct hv_vcpu_zone) == 0x8000, "hv_vcpu_zone");
 static_assert(sizeof(struct hv_vcpu_zone) == 0x8000, "hv_vcpu_zone");
 
 
 struct hv_vcpu_data {
 struct hv_vcpu_data {
-  struct hv_vcpu_zone* vcpu_zone;  // 0x0
-  uint64_t aa64dfr0_el1;           // 0x8
-  uint64_t aa64dfr1_el1;           // 0x10
-  uint64_t aa64isar0_el1;          // 0x18
-  uint64_t aa64isar1_el1;          // 0x20
-  uint64_t aa64mmfr0_el1;          // 0x28
-  uint64_t aa64mmfr1_el1;          // 0x30
-  uint64_t aa64mmfr2_el1;          // 0x38
-  uint64_t aa64pfr0_el1;           // 0x40
-  uint64_t aa64pfr1_el1;           // 0x48
-  char filler[0xe8 - 0x50];        // 0x50
-  uint64_t pending_interrupts;     // 0xe8
-  hv_vcpu_exit_t exit;             // 0xf0
-  char filler2[0x8];               // 0x110
+  struct hv_vcpu_zone* vcpu_zone;                 // 0x0
+  struct hv_vcpu_data_feature_regs feature_regs;  // 0x8
+  char filler[0xe8 - 0x50];                       // 0x50
+  uint64_t pending_interrupts;                    // 0xe8
+  hv_vcpu_exit_t exit;                            // 0xf0
+  char filler2[0x8];                              // 0x110
 };
 };
 
 
 static_assert(sizeof(struct hv_vcpu_data) == 0x118, "hv_vcpu_data");
 static_assert(sizeof(struct hv_vcpu_data) == 0x118, "hv_vcpu_data");
@@ -168,7 +205,22 @@ hv_return_t hv_vcpu_create(hv_vcpu_t* vcpu, hv_vcpu_exit_t** exit, hv_vcpu_confi
   *vcpu = cpuid;
   *vcpu = cpuid;
   *exit = &vcpu_data->exit;
   *exit = &vcpu_data->exit;
   pthread_mutex_unlock(&vcpus_mutex);
   pthread_mutex_unlock(&vcpus_mutex);
-  // TODO(zhuowei): configure regs
+  // TODO(zhuowei): configure regs from HV_CALL_VM_GET_CAPABILITIES
+  err = _hv_vcpu_config_get_feature_regs(&vcpu_data->feature_regs);
+  if (err) {
+    hv_vcpu_destroy(cpuid);
+    return err;
+  }
+  // TODO(zhuowei): set vmkeyhi_el2/vmkeylo_el2
+
+  // Apple traps PMCCNTR_EL0 using this proprietary register, then translates the syndrome.
+  // No, I don't know why Apple doesn't just use HDFGRTR_EL2 or MDCR_EL2
+  vcpu_data->vcpu_zone->rw.controls.hacr_el2 |= 1ull << 56;
+  // TID3: trap the feature regs so we can handle these ourselves
+  // TODO(zhuowei): or not... we don't handle these yet!
+  // vcpu_data->vcpu_zone->rw.controls.hcr_el2 |= 0x40000ull;
+  // TODO(zhuowei): if ro hacr has a bit set, clear rw hcr_el2 TIDCP?!
+  vcpu_data->vcpu_zone->rw.state_dirty |= 0x4;
   return 0;
   return 0;
 }
 }
 
 
@@ -220,6 +272,11 @@ hv_return_t hv_vcpu_run(hv_vcpu_t vcpu) {
         exit->exception.virtual_address = vcpu_data->vcpu_zone->ro.exit.vmexit_far;
         exit->exception.virtual_address = vcpu_data->vcpu_zone->ro.exit.vmexit_far;
         exit->exception.physical_address = vcpu_data->vcpu_zone->ro.exit.vmexit_hpfar;
         exit->exception.physical_address = vcpu_data->vcpu_zone->ro.exit.vmexit_hpfar;
         // TODO(zhuowei): handle registers
         // TODO(zhuowei): handle registers
+        // TODO(zhuowei): this is just one case in that massive switch statement!
+        if ((exit->exception.syndrome >> 26) == 0b111111) {
+          exit->exception.syndrome =
+              0x62000000 | (vcpu_data->vcpu_zone->ro.exit.vmexit_instr & 0x1ffffff);
+        }
         break;
         break;
       }
       }
       case 3:
       case 3:
@@ -328,7 +385,7 @@ static bool find_sys_reg(hv_sys_reg_t sys_reg, uint64_t* offset, uint64_t* sync_
   return true;
   return true;
 }
 }
 
 
-//static_assert(offsetof(arm_guest_rw_context_t, dbgregs.bp[0].bvr) == 0x450,
+// static_assert(offsetof(arm_guest_rw_context_t, dbgregs.bp[0].bvr) == 0x450,
 //              "HV_SYS_REG_DBGBVR0_EL1");
 //              "HV_SYS_REG_DBGBVR0_EL1");
 
 
 hv_return_t hv_vcpu_get_sys_reg(hv_vcpu_t vcpu, hv_sys_reg_t sys_reg, uint64_t* value) {
 hv_return_t hv_vcpu_get_sys_reg(hv_vcpu_t vcpu, hv_sys_reg_t sys_reg, uint64_t* value) {
@@ -342,31 +399,31 @@ hv_return_t hv_vcpu_get_sys_reg(hv_vcpu_t vcpu, hv_sys_reg_t sys_reg, uint64_t*
       *value = vcpu_zone->rw.controls.vmpidr_el2;
       *value = vcpu_zone->rw.controls.vmpidr_el2;
       return 0;
       return 0;
     case HV_SYS_REG_ID_AA64PFR0_EL1:
     case HV_SYS_REG_ID_AA64PFR0_EL1:
-      *value = vcpu_data->aa64pfr0_el1;
+      *value = vcpu_data->feature_regs.aa64pfr0_el1;
       return 0;
       return 0;
     case HV_SYS_REG_ID_AA64PFR1_EL1:
     case HV_SYS_REG_ID_AA64PFR1_EL1:
-      *value = vcpu_data->aa64pfr1_el1;
+      *value = vcpu_data->feature_regs.aa64pfr1_el1;
       return 0;
       return 0;
     case HV_SYS_REG_ID_AA64DFR0_EL1:
     case HV_SYS_REG_ID_AA64DFR0_EL1:
-      *value = vcpu_data->aa64dfr0_el1;
+      *value = vcpu_data->feature_regs.aa64dfr0_el1;
       return 0;
       return 0;
     case HV_SYS_REG_ID_AA64DFR1_EL1:
     case HV_SYS_REG_ID_AA64DFR1_EL1:
-      *value = vcpu_data->aa64dfr1_el1;
+      *value = vcpu_data->feature_regs.aa64dfr1_el1;
       return 0;
       return 0;
     case HV_SYS_REG_ID_AA64ISAR0_EL1:
     case HV_SYS_REG_ID_AA64ISAR0_EL1:
-      *value = vcpu_data->aa64isar0_el1;
+      *value = vcpu_data->feature_regs.aa64isar0_el1;
       return 0;
       return 0;
     case HV_SYS_REG_ID_AA64ISAR1_EL1:
     case HV_SYS_REG_ID_AA64ISAR1_EL1:
-      *value = vcpu_data->aa64isar1_el1;
+      *value = vcpu_data->feature_regs.aa64isar1_el1;
       return 0;
       return 0;
     case HV_SYS_REG_ID_AA64MMFR0_EL1:
     case HV_SYS_REG_ID_AA64MMFR0_EL1:
-      *value = vcpu_data->aa64mmfr0_el1;
+      *value = vcpu_data->feature_regs.aa64mmfr0_el1;
       return 0;
       return 0;
     case HV_SYS_REG_ID_AA64MMFR1_EL1:
     case HV_SYS_REG_ID_AA64MMFR1_EL1:
-      *value = vcpu_data->aa64mmfr1_el1;
+      *value = vcpu_data->feature_regs.aa64mmfr1_el1;
       return 0;
       return 0;
     case HV_SYS_REG_ID_AA64MMFR2_EL1:
     case HV_SYS_REG_ID_AA64MMFR2_EL1:
-      *value = vcpu_data->aa64mmfr2_el1;
+      *value = vcpu_data->feature_regs.aa64mmfr2_el1;
       return 0;
       return 0;
     default:
     default:
       break;
       break;
@@ -403,31 +460,31 @@ hv_return_t hv_vcpu_set_sys_reg(hv_vcpu_t vcpu, hv_sys_reg_t sys_reg, uint64_t v
     }
     }
       // the kernel doesn't set these - userspace traps and handles these
       // the kernel doesn't set these - userspace traps and handles these
     case HV_SYS_REG_ID_AA64PFR0_EL1:
     case HV_SYS_REG_ID_AA64PFR0_EL1:
-      vcpu_data->aa64pfr0_el1 = value;
+      vcpu_data->feature_regs.aa64pfr0_el1 = value;
       return 0;
       return 0;
     case HV_SYS_REG_ID_AA64PFR1_EL1:
     case HV_SYS_REG_ID_AA64PFR1_EL1:
-      vcpu_data->aa64pfr1_el1 = value;
+      vcpu_data->feature_regs.aa64pfr1_el1 = value;
       return 0;
       return 0;
     case HV_SYS_REG_ID_AA64DFR0_EL1:
     case HV_SYS_REG_ID_AA64DFR0_EL1:
-      vcpu_data->aa64dfr0_el1 = value;
+      vcpu_data->feature_regs.aa64dfr0_el1 = value;
       return 0;
       return 0;
     case HV_SYS_REG_ID_AA64DFR1_EL1:
     case HV_SYS_REG_ID_AA64DFR1_EL1:
-      vcpu_data->aa64dfr1_el1 = value;
+      vcpu_data->feature_regs.aa64dfr1_el1 = value;
       return 0;
       return 0;
     case HV_SYS_REG_ID_AA64ISAR0_EL1:
     case HV_SYS_REG_ID_AA64ISAR0_EL1:
-      vcpu_data->aa64isar0_el1 = value;
+      vcpu_data->feature_regs.aa64isar0_el1 = value;
       return 0;
       return 0;
     case HV_SYS_REG_ID_AA64ISAR1_EL1:
     case HV_SYS_REG_ID_AA64ISAR1_EL1:
-      vcpu_data->aa64isar1_el1 = value;
+      vcpu_data->feature_regs.aa64isar1_el1 = value;
       return 0;
       return 0;
     case HV_SYS_REG_ID_AA64MMFR0_EL1:
     case HV_SYS_REG_ID_AA64MMFR0_EL1:
-      vcpu_data->aa64mmfr0_el1 = value;
+      vcpu_data->feature_regs.aa64mmfr0_el1 = value;
       return 0;
       return 0;
     case HV_SYS_REG_ID_AA64MMFR1_EL1:
     case HV_SYS_REG_ID_AA64MMFR1_EL1:
-      vcpu_data->aa64mmfr1_el1 = value;
+      vcpu_data->feature_regs.aa64mmfr1_el1 = value;
       return 0;
       return 0;
     case HV_SYS_REG_ID_AA64MMFR2_EL1:
     case HV_SYS_REG_ID_AA64MMFR2_EL1:
-      vcpu_data->aa64mmfr2_el1 = value;
+      vcpu_data->feature_regs.aa64mmfr2_el1 = value;
       return 0;
       return 0;
     default:
     default:
       break;
       break;

+ 41 - 0
hv_kernel_structs.h

@@ -368,3 +368,44 @@ typedef struct {
   arm_guest_ro_context_t ro;
   arm_guest_ro_context_t ro;
   arm_host_context_t priv;
   arm_host_context_t priv;
 } arm_guest_context_t;
 } arm_guest_context_t;
+
+typedef struct {
+    uint64_t api;
+    uint64_t vcpumax;
+    uint64_t control_hcr;
+    uint64_t control_hacr;
+    uint64_t control_cptr;
+    uint64_t control_mdcr;
+    uint64_t control_ich_hcr;
+    uint64_t control_vmpidr;
+    uint64_t control_vpidr;
+    uint64_t control_virtual_timer_offset;
+    uint64_t control_timer;
+    uint64_t control_vmkeyhi;
+    uint64_t control_vmkeylo;
+    uint64_t control_apsts;
+    uint64_t control_state_used;
+    uint64_t control_hfgrtr;
+    uint64_t control_hfgwtr;
+    uint64_t control_hfgitr;
+    uint64_t control_hdfgrtr;
+    uint64_t control_hdfgwtr;
+    uint64_t control_cnthctl;
+    uint64_t ctr_el0;
+    uint64_t dczid_el0;
+    uint64_t clidr_el1;
+    uint64_t ccsidr_el1_inst[8];
+    uint64_t ccsidr_el1_data_or_unified[8];
+    uint64_t id_aa64dfr0_el1;
+    uint64_t id_aa64dfr1_el1;
+    uint64_t id_aa64isar0_el1;
+    uint64_t id_aa64isar1_el1;
+    uint64_t id_aa64mmfr0_el1;
+    uint64_t id_aa64mmfr1_el1;
+    uint64_t id_aa64mmfr2_el1;
+    uint64_t id_aa64pfr0_el1;
+    uint64_t id_aa64pfr1_el1;
+    uint8_t gic_npie_active_pending_bug;
+    uint64_t ipa_bits_4k;
+    uint64_t ipa_bits_16k;
+} hv_capabilities_t;