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, ¶ms); 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, ¶ms); 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, ¶ms); 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, ¶ms); 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, ¶ms); 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