github.com/jlmucb/cloudproxy@v0.0.0-20170830161738-b5aa0b619bc4/cpvmm/vmm/dbg/vmdb.c (about)

     1  /*
     2   * Copyright (c) 2013 Intel Corporation
     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   *     http://www.apache.org/licenses/LICENSE-2.0
     8   * Unless required by applicable law or agreed to in writing, software
     9   * distributed under the License is distributed on an "AS IS" BASIS,
    10   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11   * See the License for the specific language governing permissions and
    12   * limitations under the License.
    13   */
    14  
    15  #include "file_codes.h"
    16  #define VMM_DEADLOOP()          VMM_DEADLOOP_LOG(VMDB_C)
    17  #define VMM_ASSERT(__condition) VMM_ASSERT_LOG(VMDB_C, __condition)
    18  #include "vmm_defs.h"
    19  #include "hw_utils.h"
    20  #include "vmm_dbg.h"
    21  #include "guest.h"
    22  #include "guest_cpu.h"
    23  #include "memory_allocator.h"
    24  #include "vmcs_api.h"
    25  #include "vmx_vmcs.h"
    26  #include "vmx_ctrl_msrs.h"
    27  #include "guest_cpu_vmenter_event.h"
    28  #include "vmexit.h"
    29  #include "cli.h"
    30  #include "ipc.h"
    31  #include "scheduler.h"
    32  #include "event_mgr.h"
    33  #include "vmdb.h"
    34  
    35  
    36  #ifdef VMDB_INCLUDE
    37  
    38  #define VMDB_LOG(__level, ...) VMM_LOG(mask_gdb, __level, __VA_ARGS__)
    39  
    40  #define NUMBER_OF_HW_BREAKPOINTS 4
    41  #define BREAKPOINT_ID_IS_VALID(__i) (0 <= (__i) && (__i) < 4)
    42  #define RFLAGS_RF_BIT   16
    43  #define RFLAGS_TF_BIT   8
    44  
    45  typedef struct _VMDB_THREAD_CONTEXT {
    46      ADDRESS     dr[NUMBER_OF_HW_BREAKPOINTS];               // dr:0..3
    47      ADDRESS     dr7;
    48      UINT16      skip_counter[NUMBER_OF_HW_BREAKPOINTS];
    49      BOOLEAN     sstep;                                      // single step
    50      UINT8       pad[4];
    51  } VMDB_THREAD_CONTEXT;
    52  
    53  typedef enum {
    54      VMDB_IPC_ATTACH,
    55      VMDB_IPC_DETACH,
    56      VMDB_IPC_ADD_BP,
    57      VMDB_IPC_DEL_BP,
    58      VMDB_IPC_SINGLE_STEP
    59  } VMDB_IPC_FUNCTION_ID;
    60  
    61  typedef struct _VMDB_REMOTE_PARAMS {
    62      VMDB_IPC_FUNCTION_ID            function_id;
    63      GUEST_ID                        guest_id;
    64      UINT8                           pad1[2];
    65      union {
    66          struct {
    67              ADDRESS                 linear_address;
    68              VMDB_BREAKPOINT_TYPE    bp_type;
    69              VMDB_BREAK_LENGTH_TYPE  bp_len;
    70              UINT16                  skip_counter;
    71              UINT8                   pad2[6];
    72          } add_bp;
    73          struct {
    74              ADDRESS                 linear_address;
    75          } del_bp;
    76          struct {
    77              BOOLEAN                 enable;
    78          } sstep;
    79      } u;
    80  } VMDB_REMOTE_PARAMS;
    81  
    82  
    83  /* Macros for access Debug Registers fields */
    84  
    85  #define FIELD_GET(__r, __s, __m)    (((__r) >> (__s)) & (__m))
    86  #define FIELD_CLR(__r, __t, __s, __m)    __r &= ~(((__t)(__m)) << (__s))
    87  #define FIELD_WRT(__r, __t, __s, __m, __v)                                     \
    88  {                                                                              \
    89      FIELD_CLR(__r, __t, __s, __m);                                             \
    90      __r |= ((__t) ((__v) & (__m))) << (__s);                                   \
    91  }
    92  
    93  #define DR7_LOCAL_BIT(__x)          (__x) * 2
    94  #define DR7_LOCAL_GET(__r, __x)     BIT_GET64(__r, DR7_LOCAL_BIT(__x))
    95  #define DR7_LOCAL_SET(__r, __x)     BIT_SET64(__r, DR7_LOCAL_BIT(__x))
    96  #define DR7_LOCAL_CLR(__r, __x)     BIT_CLR64(__r, DR7_LOCAL_BIT(__x))
    97  
    98  #define DR7_GLOBAL_BIT(__x)         ((__x) * 2 + 1)
    99  #define DR7_GLOBAL_GET(__r, __x)    BIT_GET64(__r, DR7_GLOBAL_BIT(__x))
   100  #define DR7_GLOBAL_SET(__r, __x)    BIT_SET64(__r, DR7_GLOBAL_BIT(__x))
   101  #define DR7_GLOBAL_CLR(__r, __x)    BIT_CLR64(__r, DR7_GLOBAL_BIT(__x))
   102  
   103  #define DR7_GD_BIT                  13
   104  #define DR7_GD_GET(__r)             BIT_GET64(__r, DR7_GD_BIT)
   105  #define DR7_GD_SET(__r)             BIT_SET64(__r, DR7_GD_BIT)
   106  #define DR7_GD_CLR(__r)             BIT_CLR64(__r, DR7_GD_BIT)
   107  
   108  
   109  #define DR7_RW_MASK                 3
   110  #define DR7_RW_SHIFT(__x)           (16 + ((__x) & DR7_RW_MASK) * 4)
   111  #define DR7_RW_GET(__r, __x)        FIELD_GET(__r, DR7_RW_SHIFT(__x), DR7_RW_MASK)
   112  #define DR7_RW_CLR(__r, __x)        FIELD_CLR(__r, ADDRESS, DR7_RW_SHIFT(__x), DR7_RW_MASK)
   113  #define DR7_RW_WRT(__r, __x, __rw)  FIELD_WRT(__r, ADDRESS, DR7_RW_SHIFT(__x), DR7_RW_MASK, __rw)
   114  #define DR7_ONES                    0x700
   115  
   116  
   117  #define DR7_LEN_MASK                3
   118  #define DR7_LEN_SHIFT(__x)          (18 + ((__x) & DR7_LEN_MASK) * 4)
   119  #define DR7_LEN_GET(__r, __x)       FIELD_GET(__r, DR7_LEN_SHIFT(__x), DR7_LEN_MASK)
   120  #define DR7_LEN_CLR(__r, __x)       FIELD_CLR(__r, ADDRESS, DR7_LEN_SHIFT(__x), DR7_LEN_MASK)
   121  #define DR7_LEN_WRT(__r, __x, __rw) FIELD_WRT(__r, ADDRESS, DR7_LEN_SHIFT(__x), DR7_LEN_MASK, __rw)
   122  
   123  #define DR7_MUST_ONE_BITS           0x400
   124  
   125  
   126  #define DR6_BP(__x)         (1 << (__x))
   127  #define DR6_BD              (1 << 13)
   128  #define DR6_BS              (1 << 14)
   129  #define DR6_BT              (1 << 15)
   130  
   131  
   132  static char * bp_type_name[] = { "exe", "write", "io", "rw" };
   133  static UINT8 bp_actual_length[] = { 1, 2, 8, 4 };
   134  static VMM_IA32_GP_REGISTERS lkup_operand[] =
   135      {
   136      IA32_REG_RAX,
   137      IA32_REG_RCX,
   138      IA32_REG_RDX,
   139      IA32_REG_RBX,
   140      IA32_REG_RSP,
   141      IA32_REG_RBP,
   142      IA32_REG_RSI,
   143      IA32_REG_RDI,
   144      IA32_REG_R8,
   145      IA32_REG_R9,
   146      IA32_REG_R10,
   147      IA32_REG_R11,
   148      IA32_REG_R12,
   149      IA32_REG_R13,
   150      IA32_REG_R14,
   151      IA32_REG_R15
   152      };
   153  
   154  
   155  static VMDB_THREAD_CONTEXT * vmdb_thread_context_create(void);
   156  static void vmdb_thread_context_destroy(VMDB_THREAD_CONTEXT *vmdb);
   157  static int  vmdb_breakpoint_lookup(VMDB_THREAD_CONTEXT *vmdb_context, ADDRESS bp_address);
   158  static int  vmdb_free_breakpoint_lookup(VMDB_THREAD_CONTEXT *vmdb_context);
   159  static VMEXIT_HANDLING_STATUS vmdb_dr_access_vmexit_handler(GUEST_CPU_HANDLE gcpu);
   160  static void vmdb_cli_init(void);
   161  static int  vmdb_cli_breakpoint_add(unsigned argc, char *args[]);
   162  static int  vmdb_cli_breakpoint_delete(unsigned argc, char *args[]);
   163  static int  vmdb_cli_breakpoint_show(unsigned argc, char *args[]);
   164  static void vmdb_fill_vmexit_request(VMEXIT_CONTROL *vmexit_request, BOOLEAN enable);
   165  static int  vmdb_cli_breakpoint_add(unsigned argc, char *args[]);
   166  static int  vmdb_cli_breakpoint_delete(unsigned argc, char *args[]);
   167  static int  vmdb_cli_breakpoint_show(unsigned argc, char *args[]);
   168  static int  vmdb_cli_debug_attach(unsigned argc, char *args[]);
   169  static int  vmdb_cli_debug_detach(unsigned argc, char *args[]);
   170  static GUEST_CPU_HANDLE vmdb_cli_locate_gcpu(char *string, BOOLEAN *apply_to_all);
   171  static void vmdb_thread_log(GUEST_CPU_HANDLE gcpu, const char *msg, const char *function_name);
   172  static void vmdb_remote_handler(CPU_ID from UNUSED, VMDB_REMOTE_PARAMS *params);
   173  static void vmdb_remote_execute(GUEST_CPU_HANDLE gcpu, VMDB_REMOTE_PARAMS *params);
   174  static void vmdb_remote_thread_attach(GUEST_CPU_HANDLE gcpu);
   175  static void vmdb_remote_thread_detach(GUEST_CPU_HANDLE    gcpu);
   176  static void vmdb_remote_breakpoint_add
   177      (
   178      GUEST_CPU_HANDLE        gcpu,
   179      ADDRESS                 linear_address,
   180      VMDB_BREAKPOINT_TYPE    bp_type,
   181      VMDB_BREAK_LENGTH_TYPE  bp_len,
   182      UINT16                  skip_counter
   183      );
   184  static void vmdb_remote_breakpoint_delete(GUEST_CPU_HANDLE gcpu, ADDRESS linear_address);
   185  static void vmdb_remote_single_step_enable(GUEST_CPU_HANDLE gcpu, BOOLEAN enable);
   186  
   187  
   188  // Disabling unreferenced formal parameter warnings
   189  #pragma warning ( push )
   190  #pragma warning ( disable : 4100 )
   191  void vmdb_thread_log(GUEST_CPU_HANDLE gcpu, const char *msg, const char *function_name)
   192      {
   193        (void)gcpu;
   194        (void)msg;
   195        (void)function_name;
   196  #if defined DEBUG || defined ENABLE_RELEASE_VMM_LOG
   197      const VIRTUAL_CPU_ID *vcpu = guest_vcpu(gcpu);
   198      VMM_ASSERT(vcpu);
   199  #endif
   200      VMDB_LOG(level_error,"%s() %s (%d,%d)\n",
   201          function_name, msg, vcpu->guest_id, vcpu->guest_cpu_id);
   202  
   203      }
   204  
   205  
   206  void vmdb_initialize(void)
   207  {
   208      vmdb_cli_init();
   209  }
   210  
   211  VMDB_THREAD_CONTEXT * vmdb_thread_context_create(void)
   212      {
   213      VMDB_THREAD_CONTEXT *vmdb = vmm_malloc(sizeof(*vmdb));
   214  
   215      if (NULL != vmdb) {
   216          vmdb->dr7 = DR7_MUST_ONE_BITS;
   217          }
   218      else {
   219          VMDB_LOG(level_error,"[vmdb] %s failed due to memory lack %d\n", __FUNCTION__);
   220          }
   221  
   222      return vmdb;
   223      }
   224  
   225  void vmdb_thread_context_destroy(VMDB_THREAD_CONTEXT *vmdb)
   226      {
   227      if (NULL != vmdb) {
   228          // remove existing breakpoint. TBD
   229          vmm_mfree(vmdb);
   230          }
   231      }
   232  
   233  
   234  // Function : vmdb_guest_initialize
   235  // Purpose  : Initialize CLI interface for vmdb.
   236  //          : Creates VMDB control structure at the guest and
   237  //          : install VMDB VMEXIT handler on INT1.
   238  // Arguments: GUEST_ID
   239  // Returns  : VMM_STATUS
   240  VMM_STATUS vmdb_guest_initialize(GUEST_ID guest_id)
   241      {
   242      return vmexit_install_handler ( guest_id, vmdb_dr_access_vmexit_handler,
   243          Ia32VmxExitBasicReasonDrAccess);
   244      }
   245  
   246  // Function : vmdb_thread_attach
   247  // Purpose  : Enables VMDB on all given GCPU
   248  // Arguments: GUEST_CPU_HANDLE
   249  // Returns  : VMM_STATUS
   250  VMM_STATUS vmdb_thread_attach(GUEST_CPU_HANDLE gcpu)
   251  {
   252      VMM_STATUS status = VMM_ERROR;
   253      VMDB_THREAD_CONTEXT  *vmdb = gcpu_get_vmdb(gcpu);
   254      VMEXIT_CONTROL       vmexit_request;
   255  
   256      do  { // one-shot loop
   257          if (NULL != vmdb) {
   258              vmdb_thread_log(gcpu, "VMDB already attached to thread", __FUNCTION__);
   259              break;
   260              }
   261  
   262          vmdb = vmdb_thread_context_create();
   263          if (NULL == vmdb) {
   264              vmdb_thread_log(gcpu, "VMDB failed to create context for thread", __FUNCTION__);
   265              break;
   266              }
   267  
   268          gcpu_set_vmdb(gcpu, vmdb);
   269  
   270          vmdb_fill_vmexit_request(&vmexit_request, TRUE);
   271          gcpu_control_setup(gcpu, &vmexit_request);
   272  
   273          status = VMM_OK;
   274  
   275          } while (0);
   276  
   277      return status;
   278      }
   279  
   280  
   281  // Function : vmdb_thread_detach
   282  // Purpose  : Disables VMDB on all given GCPU
   283  // Arguments: GUEST_CPU_HANDLE
   284  // Returns  : VMM_STATUS
   285  VMM_STATUS vmdb_thread_detach(GUEST_CPU_HANDLE gcpu)
   286      {
   287      VMM_STATUS          status = VMM_ERROR;
   288      VMDB_THREAD_CONTEXT *vmdb = gcpu_get_vmdb(gcpu);
   289      VMEXIT_CONTROL      vmexit_request;
   290  
   291      do  {  // one-shot loop
   292          if (NULL == vmdb) {
   293              vmdb_thread_log(gcpu, "VMDB already detached from thread", __FUNCTION__);
   294              break;
   295          }
   296  
   297          vmdb_fill_vmexit_request(&vmexit_request, FALSE);
   298          gcpu_control_setup(gcpu, &vmexit_request);
   299  
   300          vmdb_breakpoint_delete_all(gcpu);
   301          vmdb_single_step_enable(gcpu, FALSE);
   302          vmdb_settings_apply_to_hw(gcpu);
   303  
   304          vmdb_thread_context_destroy(vmdb);
   305          gcpu_set_vmdb(gcpu, NULL);
   306  
   307          } while (0);
   308  
   309      return status;
   310      }
   311  
   312  
   313  // Function : vmdb_fill_vmexit_request
   314  // Purpose  : Configures VMDB-related VTx controls, depending on enble value
   315  //          :   DR-access
   316  //          :   Save/Load DR
   317  //          :   Exception on INT1
   318  // Arguments: VMEXIT_CONTROL *vmexit_request
   319  //          : BOOLEAN enable/disable
   320  // Returns  : void
   321  void vmdb_fill_vmexit_request ( OUT VMEXIT_CONTROL *vmexit_request, BOOLEAN enable)
   322      {
   323      IA32_VMCS_EXCEPTION_BITMAP            exceptions_mask;
   324      PROCESSOR_BASED_VM_EXECUTION_CONTROLS exec_controls_mask;
   325      VM_EXIT_CONTROLS                      vmexit_controls;
   326      VM_ENTRY_CONTROLS                     vmenter_controls;
   327      UINT32                                value = enable ? (UINT32)-1 : 0;
   328  
   329      vmm_memset( vmexit_request, 0, sizeof( VMEXIT_CONTROL ));
   330      exceptions_mask.Uint32  = 0;
   331      exceptions_mask.Bits.DB = 1;
   332      vmexit_request->exceptions.bit_mask = exceptions_mask.Uint32;
   333      vmexit_request->exceptions.bit_request = value;
   334  
   335      exec_controls_mask.Uint32      = 0;
   336      exec_controls_mask.Bits.MovDr  = 1;
   337      vmexit_request->proc_ctrls.bit_mask = exec_controls_mask.Uint32;
   338      vmexit_request->proc_ctrls.bit_request = value;
   339  
   340      vmexit_controls.Uint32                 = 0;
   341      vmexit_controls.Bits.SaveDebugControls = 1;
   342      vmexit_request->vm_exit_ctrls.bit_mask = vmexit_controls.Uint32;
   343      vmexit_request->vm_exit_ctrls.bit_request = value;
   344  
   345      vmenter_controls.Uint32                 = 0;
   346      vmenter_controls.Bits.LoadDebugControls = 1;
   347  
   348      vmexit_request->vm_enter_ctrls.bit_mask = vmenter_controls.Uint32;
   349      vmexit_request->vm_enter_ctrls.bit_request = value;
   350  
   351      }
   352  
   353  
   354  VMM_STATUS vmdb_single_step_enable(GUEST_CPU_HANDLE gcpu, BOOLEAN enable)
   355      {
   356      VMDB_THREAD_CONTEXT   *vmdb;
   357      VMM_STATUS          status;
   358  
   359      VMM_ASSERT(gcpu);
   360  
   361      if (NULL != (vmdb = gcpu_get_vmdb(gcpu))) {
   362          vmdb->sstep = enable;
   363          status = VMM_OK;
   364          }
   365      else {
   366          vmdb_thread_log(gcpu, "gDB is not attached to thread", __FUNCTION__);
   367          status = VMM_ERROR;
   368          }
   369      return status;
   370      }
   371  
   372  
   373  VMM_STATUS  vmdb_single_step_info(GUEST_CPU_HANDLE gcpu, BOOLEAN *enable)
   374      {
   375      VMDB_THREAD_CONTEXT   *vmdb;
   376      VMM_STATUS          status;
   377  
   378      VMM_ASSERT(gcpu);
   379      if (NULL != (vmdb = gcpu_get_vmdb(gcpu))) {
   380          if (enable != NULL)
   381              *enable = vmdb->sstep;
   382          status = VMM_OK;
   383          }
   384      else {
   385          vmdb_thread_log(gcpu, "gDB is not attached to thread", __FUNCTION__);
   386          status = VMM_ERROR;
   387          }
   388      return status;
   389      }
   390  
   391  // Function : vmdb_breakpoint_lookup
   392  // Purpose  : Look for breakpoint equal to given address
   393  // Arguments: VMDB_THREAD_CONTEXT *vmdb_context - where to search
   394  //          : ADDRESS            bp_address  - what to look for
   395  // Returns  : Breakpoint ID if found, -1 if not
   396  int vmdb_breakpoint_lookup ( VMDB_THREAD_CONTEXT *vmdb_context, ADDRESS  bp_address)
   397      {
   398      int i;
   399      for (i = 0; i < NUMBER_OF_HW_BREAKPOINTS; ++i) {
   400          if (bp_address == vmdb_context->dr[i]) {
   401              return i;   // found
   402              }
   403          }
   404      return -1;  // not found
   405      }
   406  
   407  
   408  // Function : vmdb_free_breakpoint_lookup
   409  // Purpose  : Look for free (not used) breakpoint. Wrapper upon vmdb_breakpoint_lookup.
   410  // Arguments: VMDB_THREAD_CONTEXT *vmdb_context - where to search
   411  // Returns  : Breakpoint ID if found, -1 if not
   412  int vmdb_free_breakpoint_lookup(VMDB_THREAD_CONTEXT *vmdb_context)
   413      {
   414      return vmdb_breakpoint_lookup(vmdb_context, 0);
   415      }
   416  
   417  
   418  // Function : vmdb_settings_apply_to_hw
   419  // Purpose  : Update GCPU DRs from its guest's VMDB context
   420  // Arguments: GUEST_CPU_HANDLE gcpu
   421  // Returns  : void
   422  void vmdb_settings_apply_to_hw ( GUEST_CPU_HANDLE gcpu)
   423      {
   424      VMDB_THREAD_CONTEXT *vmdb = gcpu_get_vmdb(gcpu);
   425  
   426      if (NULL != vmdb) {
   427          UINT64      rflags;
   428          VMCS_OBJECT *vmcs = gcpu_get_vmcs(gcpu);
   429  
   430          gcpu_set_debug_reg(gcpu, IA32_REG_DR7, vmdb->dr7);
   431          gcpu_set_debug_reg(gcpu, IA32_REG_DR0, vmdb->dr[0]);
   432          gcpu_set_debug_reg(gcpu, IA32_REG_DR1, vmdb->dr[1]);
   433          gcpu_set_debug_reg(gcpu, IA32_REG_DR2, vmdb->dr[2]);
   434          gcpu_set_debug_reg(gcpu, IA32_REG_DR3, vmdb->dr[3]);
   435  
   436          rflags = vmcs_read(vmcs, VMCS_GUEST_RFLAGS);
   437          if (vmdb->sstep)
   438              BIT_SET64(rflags, RFLAGS_TF_BIT);
   439          else
   440              BIT_CLR64(rflags, RFLAGS_TF_BIT);
   441          vmcs_write(vmcs, VMCS_GUEST_RFLAGS, rflags);
   442          }
   443      }
   444  
   445  
   446  // Function : vmdb_breakpoint_info
   447  // Purpose  : Gets guests breakpont info.
   448  // Arguments: self-descriptive
   449  // Returns  : VMM_STATUS
   450  VMM_STATUS vmdb_breakpoint_info ( GUEST_CPU_HANDLE gcpu, UINT32 bp_id,
   451      ADDRESS *linear_address, VMDB_BREAKPOINT_TYPE *bp_type, VMDB_BREAK_LENGTH_TYPE *bp_len,
   452      UINT16 *skip_counter)
   453  {
   454      VMDB_THREAD_CONTEXT     *vmdb;
   455      VMM_STATUS              status = VMM_ERROR;
   456  
   457      do  { // one-shot loop
   458          if (NULL == gcpu) {
   459              break;
   460              }
   461  
   462          vmdb = gcpu_get_vmdb(gcpu);
   463          if (NULL == vmdb) {
   464              vmdb_thread_log(gcpu, "gDB is not attached to thread", __FUNCTION__);
   465              break;
   466              }
   467  
   468          if (bp_id > NUMBER_OF_HW_BREAKPOINTS - 1)
   469              break;
   470  
   471          if (linear_address != NULL)
   472              *linear_address = vmdb->dr[bp_id];
   473          if (bp_type != NULL)
   474              *bp_type = (VMDB_BREAKPOINT_TYPE)DR7_RW_GET(vmdb->dr7, bp_id);
   475          if (bp_len != NULL)
   476              *bp_len = (VMDB_BREAK_LENGTH_TYPE)DR7_LEN_GET(vmdb->dr7, bp_id);
   477          if (skip_counter != NULL)
   478              *skip_counter = vmdb->skip_counter[bp_id];
   479  
   480          status = VMM_OK;
   481  
   482          } while (0);
   483  
   484      return status;
   485      }
   486  
   487  
   488  // Function : vmdb_breakpoint_add
   489  // Purpose  : Add breakpont to guest, and propagate it to all guest's GCPUs
   490  // Arguments: self-descriptive
   491  // Returns  : VMM_STATUS
   492  VMM_STATUS vmdb_breakpoint_add (GUEST_CPU_HANDLE gcpu,
   493      ADDRESS  linear_address, VMDB_BREAKPOINT_TYPE bp_type,
   494      VMDB_BREAK_LENGTH_TYPE  bp_len, UINT16   skip_counter)
   495  {
   496      int                     bp_id;
   497      VMDB_THREAD_CONTEXT     *vmdb;
   498      VMM_STATUS              status = VMM_ERROR;
   499  
   500      do  { // one-shot loop
   501          if (NULL == gcpu) {
   502              break;
   503              }
   504  
   505          vmdb = gcpu_get_vmdb(gcpu);
   506          if (NULL == vmdb) {
   507              vmdb_thread_log(gcpu, "gDB is not attached to thread", __FUNCTION__);
   508              break;
   509              }
   510  
   511          if(((int)bp_type)<VMDB_BREAK_TYPE_FIRST || bp_type>VMDB_BREAK_TYPE_LAST) {
   512              VMDB_LOG(level_warning,"[vmdb] Invalid break type(%d)\n", bp_type);
   513              break;
   514              }
   515  
   516          if (VMDB_BREAK_ON_EXE == bp_type) {
   517              bp_len = VMDB_BREAK_LENGTH_1;
   518              }
   519  
   520          if (((int)bp_len)<VMDB_BREAK_LENGTH_FIRST || bp_len>VMDB_BREAK_LENGTH_LAST) {
   521              VMDB_LOG(level_warning,"[vmdb] Invalid break length(%d)\n", bp_len);
   522              break;
   523              }
   524  
   525          bp_id = vmdb_free_breakpoint_lookup(vmdb);
   526          if ( ! BREAKPOINT_ID_IS_VALID(bp_id))
   527              {
   528              VMDB_LOG(level_trace,"[vmdb] No room to set new breakpoint\n");
   529              break;
   530              }
   531  
   532          // here free entry for breakpoint is found, so update VMDB context
   533          vmdb->dr[bp_id] = linear_address;
   534          vmdb->skip_counter[bp_id] = skip_counter;
   535          DR7_GLOBAL_SET(vmdb->dr7, bp_id);
   536          DR7_RW_WRT (vmdb->dr7, bp_id, bp_type);
   537          DR7_LEN_WRT(vmdb->dr7, bp_id, bp_len);
   538          vmdb->dr7 |= DR7_MUST_ONE_BITS;
   539  
   540          status = VMM_OK;
   541  
   542          } while (0);
   543  
   544      return status;
   545      }
   546  
   547  
   548  // Function : vmdb_breakpoint_delete
   549  // Purpose  : Delete breakpont at guest context and from all its GCPUs
   550  // Arguments: GUEST_HANDLE  guest
   551  //          : ADDRESS linear_address - address of breakpoint to delete
   552  //          : if equal -1, then remove all breakpoints for this guest
   553  // Returns  : VMM_STATUS
   554  VMM_STATUS vmdb_breakpoint_delete ( GUEST_CPU_HANDLE gcpu, ADDRESS  linear_address)
   555      {
   556      VMDB_THREAD_CONTEXT *vmdb;
   557      int                 bp_id;
   558      int                 bp_from;
   559      int                 bp_to;
   560      VMM_STATUS          status = VMM_ERROR;
   561  
   562      do {  // one-shot loop
   563          if (NULL == gcpu) {
   564              break;
   565              }
   566  
   567          vmdb = gcpu_get_vmdb(gcpu);
   568          if (NULL == vmdb) {
   569              vmdb_thread_log(gcpu, "VMDB is not attached to thread", __FUNCTION__);
   570              break;
   571              }
   572  
   573          if ((ADDRESS) -1 == linear_address) {
   574              bp_from = 0;
   575              bp_to = NUMBER_OF_HW_BREAKPOINTS-1;
   576              }
   577          else {
   578              bp_from = vmdb_breakpoint_lookup(vmdb, linear_address);
   579              bp_to = bp_from;
   580              }
   581  
   582          if ( ! BREAKPOINT_ID_IS_VALID(bp_from)) {
   583              vmdb_thread_log(gcpu, "VMDB did not find breakpoint on thread", __FUNCTION__);
   584              break;
   585              }
   586  
   587          for (bp_id = bp_from; bp_id <= bp_to; ++bp_id) {
   588              vmdb->dr[bp_id] = 0;
   589              DR7_LOCAL_CLR (vmdb->dr7, bp_id);
   590              DR7_GLOBAL_CLR(vmdb->dr7, bp_id);
   591              }
   592  
   593          status = VMM_OK;
   594          } while(0);
   595  
   596      return status;
   597      }
   598  
   599  
   600  VMM_STATUS vmdb_breakpoint_delete_all(GUEST_CPU_HANDLE gcpu)
   601      {
   602      return vmdb_breakpoint_delete(gcpu, (ADDRESS) -1);
   603      }
   604  
   605  BOOLEAN vmdb_exception_handler(GUEST_CPU_HANDLE gcpu)
   606      {
   607      VMDB_THREAD_CONTEXT    *vmdb = gcpu_get_vmdb(gcpu);
   608      VMCS_OBJECT            *vmcs = gcpu_get_vmcs(gcpu);
   609      IA32_VMX_EXIT_QUALIFICATION qualification;
   610      ADDRESS                 guest_rflags;
   611      int                     i;
   612  #if defined DEBUG || defined ENABLE_RELEASE_VMM_LOG
   613      const VIRTUAL_CPU_ID   *vcpu = guest_vcpu(gcpu);
   614      VMM_ASSERT(vcpu);
   615  #endif
   616  
   617      VMM_ASSERT(vmdb);
   618  
   619      qualification.Uint64 = vmcs_read(vmcs, VMCS_EXIT_INFO_QUALIFICATION);
   620  
   621      if (qualification.DbgException.DbgRegAccess) {
   622          VMDB_LOG(level_print_always,"[vmdb] Debug Registers Access is NOT supported\n");
   623          }
   624  
   625      if (qualification.DbgException.SingleStep) {
   626          vmdb_thread_log(gcpu, "VMDB Single Step Break occurred on thread", __FUNCTION__);
   627  
   628          if (FALSE == event_raise(EVENT_GUEST_CPU_BREAKPOINT, gcpu, 0)) {
   629              VMM_DEADLOOP();
   630              }
   631          }
   632      else {
   633              for (i = 0; i < NUMBER_OF_HW_BREAKPOINTS; ++i) {
   634                  if (BIT_GET64(qualification.DbgException.BreakPoints, i)) {
   635  #if defined DEBUG || defined ENABLE_RELEASE_VMM_LOG
   636                      UINT32 db_type = (UINT32) DR7_RW_GET(vmdb->dr7, i);
   637  #endif
   638  
   639                      if (0 != vmdb->skip_counter[i]) {
   640  		      (void)vmdb->skip_counter[i];
   641                          continue;
   642                          }
   643  
   644  		    (void)bp_type_name;
   645  		    (void)bp_actual_length;
   646                      VMDB_LOG(level_print_always,"[vmdb] %s break occurred at address(%P) on thread(%d,%d)\n",
   647                          bp_type_name[db_type], vmdb->dr[i],
   648                          vcpu->guest_id, vcpu->guest_id);
   649  
   650                      // If it is breakpoint for the VMDB STUB, then propagate it.
   651                      if (FALSE == event_raise(EVENT_GUEST_CPU_SINGLE_STEP, gcpu, 0)) {
   652                          VMM_DEADLOOP();
   653                          }
   654                      }
   655                  }
   656          }
   657  
   658      // Set Resume Flag to prevent breakpoint stucking
   659      guest_rflags = gcpu_get_native_gp_reg(gcpu, IA32_REG_RFLAGS);
   660      BIT_SET64(guest_rflags, RFLAGS_RF_BIT);
   661      gcpu_set_native_gp_reg(gcpu, IA32_REG_RFLAGS, guest_rflags);
   662  
   663      gcpu_vmexit_exception_resolve(gcpu);
   664  
   665      return TRUE;
   666      }
   667  
   668  VMEXIT_HANDLING_STATUS vmdb_dr_access_vmexit_handler(GUEST_CPU_HANDLE gcpu)
   669      {
   670      VMCS_OBJECT                 *vmcs = gcpu_get_vmcs(gcpu);
   671      IA32_VMX_EXIT_QUALIFICATION qualification;
   672      int                         dbreg_id;
   673      VMM_IA32_GP_REGISTERS       gpreg_id;
   674  
   675      qualification.Uint64 = vmcs_read(vmcs, VMCS_EXIT_INFO_QUALIFICATION);
   676      gpreg_id = lkup_operand[qualification.DrAccess.MoveGpr];
   677      dbreg_id = (int) qualification.DrAccess.Number;
   678      if (6 == dbreg_id) dbreg_id = IA32_REG_DR6;
   679      if (7 == dbreg_id) dbreg_id = IA32_REG_DR7;
   680  
   681      if  (0 == qualification.DrAccess.Direction) {
   682          // do nothing
   683          }
   684      else {
   685          UINT64 reg_value = gcpu_get_debug_reg(gcpu, (VMM_IA32_DEBUG_REGISTERS)dbreg_id);
   686          gcpu_set_native_gp_reg(gcpu, gpreg_id, reg_value);
   687          }
   688  
   689      gcpu_skip_guest_instruction(gcpu);
   690      return VMEXIT_HANDLED;
   691      }
   692  
   693  
   694  void vmdb_remote_handler (CPU_ID from UNUSED, VMDB_REMOTE_PARAMS *params)
   695  {
   696      VIRTUAL_CPU_ID      vcpu;
   697      GUEST_CPU_HANDLE    gcpu;
   698  
   699      do  {
   700          if (NULL == params) {
   701              VMDB_LOG(level_error,"%s called wit NULL argument\n", __FUNCTION__);
   702              break;
   703              }
   704  
   705          vcpu.guest_id     = params->guest_id;
   706          vcpu.guest_cpu_id = hw_cpu_id();
   707          if (NULL == (gcpu = gcpu_state(&vcpu))) {
   708              VMDB_LOG(level_error,"%s GCPU(%d,%d) is not found\n",
   709                  __FUNCTION__, vcpu.guest_id, vcpu.guest_cpu_id);
   710              break;
   711              }
   712  
   713          switch (params->function_id) {
   714              case VMDB_IPC_ATTACH:
   715                  vmdb_thread_attach(gcpu);
   716                  break;
   717              case VMDB_IPC_DETACH:
   718                  vmdb_thread_detach(gcpu);
   719                  break;
   720              case VMDB_IPC_ADD_BP:
   721                  vmdb_breakpoint_add ( gcpu, params->u.add_bp.linear_address,
   722                      params->u.add_bp.bp_type, params->u.add_bp.bp_len,
   723                      params->u.add_bp.skip_counter);
   724                  break;
   725              case VMDB_IPC_DEL_BP:
   726                  vmdb_breakpoint_delete(gcpu, params->u.del_bp.linear_address);
   727                  break;
   728              case VMDB_IPC_SINGLE_STEP:
   729                  vmdb_single_step_enable(gcpu, params->u.sstep.enable);
   730                  break;
   731              default:
   732                  VMDB_LOG(level_error,"%s GCPU(%d,%d) Unknown remote function ID(%d)\n",
   733                      __FUNCTION__, vcpu.guest_id, vcpu.guest_cpu_id, params->function_id);
   734                  break;
   735              }
   736  
   737          } while (0);
   738      }
   739  #pragma warning ( pop )
   740  
   741  
   742  void vmdb_remote_execute ( GUEST_CPU_HANDLE gcpu, VMDB_REMOTE_PARAMS *params)
   743      {
   744      const VIRTUAL_CPU_ID   *vcpu = guest_vcpu(gcpu);
   745  
   746      if (NULL != vcpu) {
   747          IPC_DESTINATION dst;
   748  
   749          dst.addr_shorthand = IPI_DST_ALL_EXCLUDING_SELF;
   750          params->guest_id = vcpu->guest_id;
   751          ipc_execute_handler(dst, (IPC_HANDLER_FN) vmdb_remote_handler, params);
   752          }
   753      else {
   754          VMDB_LOG(level_error,"%s Failed to locate VCPU\n", __FUNCTION__);
   755          }
   756      }
   757  
   758  void vmdb_remote_thread_attach(GUEST_CPU_HANDLE gcpu)
   759      {
   760      VMDB_REMOTE_PARAMS params;
   761  
   762      params.function_id = VMDB_IPC_ATTACH;
   763      vmdb_remote_execute(gcpu, &params);
   764      }
   765  
   766  void vmdb_remote_thread_detach(GUEST_CPU_HANDLE gcpu)
   767      {
   768      VMDB_REMOTE_PARAMS params;
   769  
   770      params.function_id = VMDB_IPC_DETACH;
   771      vmdb_remote_execute(gcpu, &params);
   772      }
   773  
   774  void vmdb_remote_breakpoint_add ( GUEST_CPU_HANDLE gcpu, ADDRESS  linear_address,
   775      VMDB_BREAKPOINT_TYPE  bp_type, VMDB_BREAK_LENGTH_TYPE  bp_len,
   776      UINT16 skip_counter)
   777      {
   778      VMDB_REMOTE_PARAMS params;
   779  
   780      params.function_id = VMDB_IPC_ADD_BP;
   781      params.u.add_bp.linear_address = linear_address;
   782      params.u.add_bp.bp_type        = bp_type;
   783      params.u.add_bp.bp_len         = bp_len;
   784      params.u.add_bp.skip_counter   = skip_counter;
   785      vmdb_remote_execute(gcpu, &params);
   786      }
   787  
   788  void vmdb_remote_breakpoint_delete ( GUEST_CPU_HANDLE  gcpu, ADDRESS linear_address)
   789      {
   790      VMDB_REMOTE_PARAMS params;
   791  
   792      params.function_id = VMDB_IPC_DEL_BP;
   793      params.u.del_bp.linear_address = linear_address;
   794      vmdb_remote_execute(gcpu, &params);
   795      }
   796  
   797  void vmdb_remote_single_step_enable ( GUEST_CPU_HANDLE gcpu, BOOLEAN enable)
   798      {
   799      VMDB_REMOTE_PARAMS params;
   800  
   801      params.function_id = VMDB_IPC_SINGLE_STEP;
   802      params.u.sstep.enable = enable;
   803      vmdb_remote_execute(gcpu, &params);
   804      }
   805  
   806  
   807  GUEST_CPU_HANDLE vmdb_cli_locate_gcpu(char *string, BOOLEAN *apply_to_all)
   808      {
   809      VIRTUAL_CPU_ID vcpu;
   810  
   811      if (NULL != apply_to_all) {
   812          if ('*' == string[0]) {
   813              *apply_to_all = TRUE;
   814              string++;   // skip '*' symbol
   815              }
   816          else {
   817              apply_to_all = FALSE;
   818              }
   819          }
   820  
   821      vcpu.guest_id = (GUEST_ID) CLI_ATOL(string);
   822      vcpu.guest_cpu_id = hw_cpu_id();
   823  
   824      return gcpu_state(&vcpu);
   825      }
   826  
   827  
   828  int vmdb_cli_breakpoint_add(unsigned argc, char *args[])
   829      {
   830      ADDRESS                 linear_address;
   831      VMDB_BREAKPOINT_TYPE    bp_type;
   832      VMDB_BREAK_LENGTH_TYPE  bp_len;
   833      UINT32                  skip_counter = 0;
   834      BOOLEAN                 apply_to_all;
   835      GUEST_CPU_HANDLE        gcpu;
   836  
   837      if (argc < 5) return -1;
   838  
   839      gcpu = vmdb_cli_locate_gcpu(args[1], &apply_to_all);
   840      if (NULL == gcpu) {
   841          CLI_PRINT("Invalid Guest %s\n", args[1]);
   842          return -1;
   843          }
   844  
   845      linear_address = CLI_ATOL64(args[2]);
   846  
   847      switch (args[3][0]) {
   848          case 'e': bp_type = VMDB_BREAK_ON_EXE; break;
   849          case 'w': bp_type = VMDB_BREAK_ON_WO;  break;
   850          case 'i': bp_type = VMDB_BREAK_ON_IO;  break;
   851          case 'r': bp_type = VMDB_BREAK_ON_RW;  break;
   852          default: return -1;
   853          }
   854  
   855      switch (args[4][0]) {
   856          case '1': bp_len = VMDB_BREAK_LENGTH_1; break;
   857          case '2': bp_len = VMDB_BREAK_LENGTH_2; break;
   858          case '4': bp_len = VMDB_BREAK_LENGTH_4; break;
   859          case '8': bp_len = VMDB_BREAK_LENGTH_8; break;
   860          default: return -1;
   861          }
   862  
   863      if (argc > 5) {
   864          skip_counter = CLI_ATOL(args[5]);
   865          }
   866  
   867      vmdb_breakpoint_add ( gcpu, linear_address, bp_type,
   868          bp_len, (UINT16) skip_counter);
   869  
   870      if (apply_to_all)
   871          {
   872          vmdb_remote_breakpoint_add ( gcpu, linear_address,
   873              bp_type, bp_len, (UINT16) skip_counter);
   874          }
   875  
   876      return 0;
   877      }
   878  
   879  int vmdb_cli_breakpoint_delete(unsigned argc, char *args[])
   880      {
   881      GUEST_CPU_HANDLE    gcpu;
   882      ADDRESS             linear_address;
   883      BOOLEAN             apply_to_all;
   884  
   885      if (argc < 3) return -1;
   886  
   887      gcpu = vmdb_cli_locate_gcpu(args[1], &apply_to_all);
   888      if (NULL == gcpu) {
   889          CLI_PRINT("Invalid Guest %s\n", args[1]);
   890          return -1;
   891          }
   892  
   893      if (0 == CLI_STRCMP("all", args[2])) {
   894          linear_address = (ADDRESS)-1;
   895          }
   896      else {
   897          linear_address = CLI_ATOL64(args[2]);
   898          }
   899  
   900      vmdb_breakpoint_delete(gcpu, linear_address);
   901  
   902      if (apply_to_all) {
   903          vmdb_remote_breakpoint_delete(gcpu, linear_address);
   904          }
   905  
   906      return 0;
   907      }
   908  
   909  int vmdb_cli_breakpoint_show(unsigned argc, char *args[])
   910      {
   911      GUEST_CPU_HANDLE    gcpu;
   912      VMDB_THREAD_CONTEXT *vmdb;
   913      ADDRESS             bp_address;
   914      int                 i;
   915  
   916      if (argc < 2) return -1;
   917  
   918      gcpu = vmdb_cli_locate_gcpu(args[1], NULL);
   919  
   920      if (NULL == gcpu) {
   921          CLI_PRINT("Invalid Guest %s\n", args[1]);
   922          return -1;
   923          }
   924  
   925      vmdb = gcpu_get_vmdb(gcpu);
   926      if (NULL == vmdb) {
   927          CLI_PRINT("VMDB is not attached to thread %s,%d\n", args[1], hw_cpu_id());
   928          return -1;
   929          }
   930  
   931      CLI_PRINT("======================================\n");
   932      CLI_PRINT("Single step:  %s\n", vmdb->sstep   ? "enabled" : "disabled");
   933      CLI_PRINT("======================================\n");
   934      CLI_PRINT("BP  linear address  type  len  counter\n");
   935      CLI_PRINT("======================================\n");
   936  
   937      for (i = 0; i < NUMBER_OF_HW_BREAKPOINTS; ++i) {
   938          CLI_PRINT("%d: ", i);
   939  
   940          bp_address = vmdb->dr[i];
   941  
   942          if (0 != bp_address && DR7_GLOBAL_GET(vmdb->dr7, i)) {
   943  #if defined DEBUG || defined ENABLE_RELEASE_VMM_LOG
   944              VMDB_BREAKPOINT_TYPE     bp_type = (VMDB_BREAKPOINT_TYPE)DR7_RW_GET(vmdb->dr7, i);
   945              VMDB_BREAK_LENGTH_TYPE   bp_len = (VMDB_BREAK_LENGTH_TYPE)DR7_LEN_GET(vmdb->dr7, i);
   946  #endif
   947              CLI_PRINT ( "%16P %5s   %d   %d", bp_address, bp_type_name[bp_type],
   948                  bp_actual_length[bp_len], vmdb->skip_counter[i]);
   949              }
   950          CLI_PRINT("\n");
   951          }
   952      return 0;
   953      }
   954  
   955  int vmdb_cli_single_step_enable(unsigned argc, char *args[])
   956      {
   957      GUEST_CPU_HANDLE    gcpu;
   958      BOOLEAN             enable;
   959      BOOLEAN             apply_to_all;
   960  
   961      if (argc < 3) {
   962          return -1;
   963          }
   964  
   965      gcpu = vmdb_cli_locate_gcpu(args[1], &apply_to_all);
   966      if (NULL == gcpu) {
   967          CLI_PRINT("Invalid Guest %s\n", args[1]);
   968          return -1;
   969          }
   970  
   971      if (CLI_IS_SUBSTR("enable", args[2])) {
   972          enable = TRUE;
   973          }
   974      else if (CLI_IS_SUBSTR("disable", args[2])) {
   975          enable = FALSE;
   976          }
   977      else {
   978          return -1;
   979          }
   980  
   981      vmdb_single_step_enable(gcpu, enable);
   982  
   983      if (apply_to_all) {
   984          vmdb_remote_single_step_enable(gcpu, enable);
   985          }
   986  
   987      return 0;
   988      }
   989  
   990  int vmdb_cli_debug_attach(unsigned argc, char *args[])
   991      {
   992      GUEST_CPU_HANDLE    gcpu;
   993      BOOLEAN             apply_to_all;
   994      GUEST_ID            guest_id;
   995  
   996      if (argc < 2) {
   997          return -1;
   998          }
   999  
  1000      gcpu = vmdb_cli_locate_gcpu(args[1], &apply_to_all);
  1001      if (NULL == gcpu) {
  1002          CLI_PRINT("Invalid Guest %s\n", args[1]);
  1003          return -1;
  1004          }
  1005  
  1006      guest_id = guest_vcpu(gcpu)->guest_id;
  1007      vmdb_guest_initialize(guest_id);
  1008      vmdb_thread_attach(gcpu);
  1009      if (apply_to_all) {
  1010              vmdb_remote_thread_attach(gcpu);
  1011          }
  1012  
  1013      return 0;
  1014      }
  1015  
  1016  
  1017  int vmdb_cli_debug_detach(unsigned argc, char *args[])
  1018      {
  1019          GUEST_CPU_HANDLE    gcpu;
  1020          BOOLEAN             apply_to_all;
  1021  
  1022          if (argc < 2) {
  1023              return -1;
  1024              }
  1025  
  1026          gcpu = vmdb_cli_locate_gcpu(args[1], &apply_to_all);
  1027          if (NULL == gcpu) {
  1028              CLI_PRINT("Invalid Guest %s\n", args[1]);
  1029              return -1;
  1030              }
  1031  
  1032          vmdb_thread_detach(gcpu);
  1033          if (apply_to_all) {
  1034                  vmdb_remote_thread_detach(gcpu);
  1035              }
  1036  
  1037          return 0;
  1038      }
  1039  
  1040  
  1041  void vmdb_cli_init(void)
  1042      {
  1043      CLI_AddCommand ( vmdb_cli_breakpoint_add, "dbg breakpoint add",
  1044          "add breakpoint in guest",
  1045          "<[*]guest> <lin addr> <e(xe)|w(rite)|i(o)|r(w)> <len> [skip count]",
  1046          CLI_ACCESS_LEVEL_USER);
  1047      CLI_AddCommand ( vmdb_cli_breakpoint_delete,
  1048          "dbg breakpoint delete", "delete breakpoint in guest",
  1049          "<[*]guest> <lin addr>", CLI_ACCESS_LEVEL_USER);
  1050      CLI_AddCommand ( vmdb_cli_single_step_enable, "dbg singlestep",
  1051          "guest single step on/off", "<[*]guest> <enable/disable>",
  1052          CLI_ACCESS_LEVEL_USER);
  1053      CLI_AddCommand ( vmdb_cli_breakpoint_show, "dbg show",
  1054          "show breakpoints set for the guest", "<guest>",
  1055          CLI_ACCESS_LEVEL_USER);
  1056      CLI_AddCommand ( vmdb_cli_debug_attach, "dbg attach", "guest debuger attach",
  1057          "<[*]guest>", CLI_ACCESS_LEVEL_USER);
  1058      CLI_AddCommand ( vmdb_cli_debug_detach, "dbg detach", "guest debuger detach",
  1059          "<[*]guest>", CLI_ACCESS_LEVEL_USER);
  1060      }
  1061  
  1062  #endif // DEBUG
  1063