Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


185497bba347a0341b37024c7c5e26709db47c2b
[palacios.git] / palacios / src / palacios / vmm_debug.c
1 /* 
2  * This file is part of the Palacios Virtual Machine Monitor developed
3  * by the V3VEE Project with funding from the United States National 
4  * Science Foundation and the Department of Energy.  
5  *
6  * The V3VEE Project is a joint project between Northwestern University
7  * and the University of New Mexico.  You can find out more at 
8  * http://www.v3vee.org
9  *
10  * Copyright (c) 2008, Jack Lange <jarusl@cs.northwestern.edu> 
11  * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org> 
12  * All rights reserved.
13  *
14  * Author: Jack Lange <jarusl@cs.northwestern.edu>
15  *
16  * This is free software.  You are permitted to use,
17  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
18  */
19
20
21 #include <palacios/vmm_debug.h>
22 #include <palacios/vmm.h>
23 #include <palacios/vmm_host_events.h>
24 #include <palacios/vm_guest.h>
25 #include <palacios/vmm_decoder.h>
26 #include <palacios/vm_guest_mem.h>
27 #include <palacios/vmm_config.h>
28
29 #define PRINT_TELEMETRY  1
30 #define PRINT_CORE_STATE 2
31 #define PRINT_ARCH_STATE 3
32 #define PRINT_STACK      4
33 #define PRINT_BACKTRACE  5
34
35
36 #define PRINT_ALL        100 // Absolutely everything
37 #define PRINT_STATE      101 // telemetry, core state, arch state
38
39
40
41
42 static int core_handler(struct guest_info * core, uint32_t cmd) {
43
44
45     switch (cmd) {
46 #ifdef V3_CONFIG_TELEMETRY
47         case PRINT_TELEMETRY: 
48             v3_print_core_telemetry(core);
49             break;
50 #endif
51         
52         case PRINT_CORE_STATE:
53             v3_raise_barrier(core->vm_info, NULL);
54
55             v3_print_guest_state(core);
56
57             v3_lower_barrier(core->vm_info);
58             break;
59         case PRINT_ARCH_STATE:
60             v3_raise_barrier(core->vm_info, NULL);
61
62             v3_print_arch_state(core);
63
64             v3_lower_barrier(core->vm_info);
65             break;
66         case PRINT_STACK:
67             v3_raise_barrier(core->vm_info, NULL);
68
69             v3_print_stack(core);
70
71             v3_lower_barrier(core->vm_info);
72             break;
73         case PRINT_BACKTRACE:
74             v3_raise_barrier(core->vm_info, NULL);
75
76             v3_print_backtrace(core);
77             
78             v3_lower_barrier(core->vm_info);
79             break;
80
81         case PRINT_STATE:
82             v3_raise_barrier(core->vm_info, NULL);
83
84 #ifdef V3_CONFIG_TELEMETRY
85             v3_print_core_telemetry(core);
86 #endif
87             v3_print_guest_state(core);
88             v3_print_arch_state(core);
89
90             v3_lower_barrier(core->vm_info);
91             break;
92
93         case PRINT_ALL:
94             v3_raise_barrier(core->vm_info, NULL);
95
96 #ifdef V3_CONFIG_TELEMETRY
97             v3_print_core_telemetry(core);
98 #endif
99             v3_print_guest_state(core);
100             v3_print_arch_state(core);
101         v3_print_stack(core);
102         v3_print_backtrace(core);
103
104             v3_lower_barrier(core->vm_info);
105             break;
106
107     }
108
109     return 0;
110 }
111
112
113 static int evt_handler(struct v3_vm_info * vm, struct v3_debug_event * evt, void * priv_data) {
114
115     V3_Print(vm, VCORE_NONE,"Debug Event Handler for core %d cmd=%x\n", evt->core_id, evt->cmd);
116
117     if (evt->core_id == -1) {
118         int i = 0;
119         for (i = 0; i < vm->num_cores; i++) {
120             core_handler(&(vm->cores[i]), evt->cmd);
121         }
122     } else {
123         return core_handler(&vm->cores[evt->core_id], evt->cmd);
124     }
125
126     
127     return 0;
128 }
129
130
131 int v3_init_vm_debugging(struct v3_vm_info * vm) {
132     v3_hook_host_event(vm, HOST_DEBUG_EVT, 
133                        V3_HOST_EVENT_HANDLER(evt_handler), 
134                        NULL);
135
136
137     return 0;
138 }
139
140
141
142
143
144 void v3_print_segments(struct v3_segments * segs) {
145     int i = 0;
146     struct v3_segment * seg_ptr;
147
148     seg_ptr=(struct v3_segment *)segs;
149   
150     char *seg_names[] = {"CS", "DS" , "ES", "FS", "GS", "SS" , "LDTR", "GDTR", "IDTR", "TR", NULL};
151     V3_Print(VM_NONE, VCORE_NONE, "Segments\n");
152
153     for (i = 0; seg_names[i] != NULL; i++) {
154
155         V3_Print(VM_NONE, VCORE_NONE, "\t%s: Sel=%x, base=%p, limit=%x (long_mode=%d, db=%d)\n", seg_names[i], seg_ptr[i].selector, 
156                    (void *)(addr_t)seg_ptr[i].base, seg_ptr[i].limit,
157                    seg_ptr[i].long_mode, seg_ptr[i].db);
158
159     }
160 }
161
162
163
164 void v3_print_ctrl_regs(struct guest_info * core) {
165     struct v3_ctrl_regs * regs = &(core->ctrl_regs);
166     int i = 0;
167     v3_reg_t * reg_ptr;
168     char * reg_names[] = {"CR0", "CR2", "CR3", "CR4", "CR8", "FLAGS", "EFER", NULL};
169    
170
171     reg_ptr = (v3_reg_t *)regs;
172
173     V3_Print(core->vm_info, core,"Ctrl Regs:\n");
174
175     for (i = 0; reg_names[i] != NULL; i++) {
176         V3_Print(core->vm_info, core, "\t%s=0x%p (at %p)\n", reg_names[i], (void *)(addr_t)reg_ptr[i], &(reg_ptr[i]));  
177     }
178
179
180 }
181
182 #if 0
183 static int safe_gva_to_hva(struct guest_info * core, addr_t linear_addr, addr_t * host_addr) {
184     /* select the proper translation based on guest mode */
185     if (core->mem_mode == PHYSICAL_MEM) {
186         if (v3_gpa_to_hva(core, linear_addr, host_addr) == -1) return -1;
187     } else if (core->mem_mode == VIRTUAL_MEM) {
188         if (v3_gva_to_hva(core, linear_addr, host_addr) == -1) return -1;
189     }
190     return 0;
191 }
192
193 static int v3_print_disassembly(struct guest_info * core) {
194     int passed_rip = 0;
195     addr_t rip, rip_linear, rip_host;
196
197     /* we don't know where the instructions preceding RIP start, so we just take
198      * a guess and hope the instruction stream synced up with our disassembly
199      * some time before RIP; if it has not we correct RIP at that point
200      */
201
202     /* start disassembly 64 bytes before current RIP, continue 32 bytes after */
203     rip = (addr_t) core->rip - 64;
204     while ((int) (rip - core->rip) < 32) {
205         V3_Print(info->vm_info, info, "disassembly step\n");
206
207         /* always print RIP, even if the instructions before were bad */
208         if (!passed_rip && rip >= core->rip) {
209             if (rip != core->rip) {
210                 V3_Print(info->vm_info, info, "***** bad disassembly up to this point *****\n");
211                 rip = core->rip;
212             }
213             passed_rip = 1;
214         }
215
216         /* look up host virtual address for this instruction */
217         rip_linear = get_addr_linear(core, rip, &(core->segments.cs));
218         if (safe_gva_to_hva(core, rip_linear, &rip_host) < 0) {
219             rip++;
220             continue;
221         }
222
223         /* print disassembled instrcution (updates rip) */
224         if (v3_disasm(core, (void *) rip_host, &rip, rip == core->rip) < 0) {
225             rip++;
226             continue;
227         }
228
229     }
230
231     return 0;
232 }
233
234 #endif
235
236 void v3_print_guest_state(struct guest_info * core) {
237     addr_t linear_addr = 0; 
238
239     V3_Print(core->vm_info, core, "RIP: %p\n", (void *)(addr_t)(core->rip));
240     linear_addr = get_addr_linear(core, core->rip, &(core->segments.cs));
241     V3_Print(core->vm_info, core, "RIP Linear: %p\n", (void *)linear_addr);
242
243     V3_Print(core->vm_info, core, "NumExits: %u\n", (uint32_t)core->num_exits);
244
245     V3_Print(core->vm_info, core, "IRQ STATE: started=%d, pending=%d\n", 
246              core->intr_core_state.irq_started, 
247              core->intr_core_state.irq_pending);
248     V3_Print(core->vm_info, core, "EXCP STATE: err_code_valid=%d, err_code=%x\n", 
249              core->excp_state.excp_error_code_valid, 
250              core->excp_state.excp_error_code);
251
252
253     v3_print_segments(&(core->segments));
254     v3_print_ctrl_regs(core);
255
256     if (core->shdw_pg_mode == SHADOW_PAGING) {
257         V3_Print(core->vm_info, core, "Shadow Paging Guest Registers:\n");
258         V3_Print(core->vm_info, core, "\tGuest CR0=%p\n", (void *)(addr_t)(core->shdw_pg_state.guest_cr0));
259         V3_Print(core->vm_info, core, "\tGuest CR3=%p\n", (void *)(addr_t)(core->shdw_pg_state.guest_cr3));
260         V3_Print(core->vm_info, core, "\tGuest EFER=%p\n", (void *)(addr_t)(core->shdw_pg_state.guest_efer.value));
261         // CR4
262     }
263     v3_print_GPRs(core);
264
265     v3_print_mem_map(core->vm_info);
266
267     v3_print_stack(core);
268
269     //  v3_print_disassembly(core);
270 }
271
272
273 void v3_print_arch_state(struct guest_info * core) {
274
275
276 }
277
278
279 void v3_print_guest_state_all(struct v3_vm_info * vm) {
280     int i = 0;
281
282     V3_Print(vm, VCORE_NONE,"VM Core states for %s\n", vm->name);
283
284     for (i = 0; i < 80; i++) {
285       V3_Print(vm, VCORE_NONE, "-");
286     }
287
288     for (i = 0; i < vm->num_cores; i++) {
289         v3_print_guest_state(&vm->cores[i]);  
290     }
291     
292     for (i = 0; i < 80; i++) {
293         V3_Print(vm, VCORE_NONE, "-");
294     }
295
296     V3_Print(vm, VCORE_NONE, "\n");    
297 }
298
299
300
301 void v3_print_stack(struct guest_info * core) {
302     addr_t linear_addr = 0;
303     addr_t host_addr = 0;
304     int i = 0;
305     v3_cpu_mode_t cpu_mode = v3_get_vm_cpu_mode(core);
306
307     linear_addr = get_addr_linear(core, core->vm_regs.rsp, &(core->segments.ss));
308  
309     V3_Print(core->vm_info, core, "Stack at %p:\n", (void *)linear_addr);
310    
311     if (core->mem_mode == PHYSICAL_MEM) {
312         if (v3_gpa_to_hva(core, linear_addr, &host_addr) == -1) {
313             PrintError(core->vm_info, core, "Could not translate Stack address\n");
314             return;
315         }
316     } else if (core->mem_mode == VIRTUAL_MEM) {
317         if (v3_gva_to_hva(core, linear_addr, &host_addr) == -1) {
318             PrintError(core->vm_info, core, "Could not translate Virtual Stack address\n");
319             return;
320         }
321     }
322     
323     V3_Print(core->vm_info, core, "Host Address of rsp = 0x%p\n", (void *)host_addr);
324  
325     // We start i at one because the current stack pointer points to an unused stack element
326     for (i = 0; i <= 24; i++) {
327
328         if (cpu_mode == REAL) {
329             V3_Print(core->vm_info, core, "\t0x%.4x\n", *((uint16_t *)host_addr + (i * 2)));
330         } else if (cpu_mode == LONG) {
331             V3_Print(core->vm_info, core, "\t%p\n", (void *)*(addr_t *)(host_addr + (i * 8)));
332         } else {
333             // 32 bit stacks...
334             V3_Print(core->vm_info, core, "\t0x%.8x\n", *(uint32_t *)(host_addr + (i * 4)));
335         }
336     }
337
338 }    
339
340
341 void v3_print_backtrace(struct guest_info * core) {
342     addr_t gla_rbp = 0;
343     int i = 0;
344     v3_cpu_mode_t cpu_mode = v3_get_vm_cpu_mode(core);
345     struct v3_cfg_file * system_map = v3_cfg_get_file(core->vm_info, "System.map");
346
347     V3_Print(core->vm_info, core, "Performing Backtrace for Core %d\n", core->vcpu_id);
348     V3_Print(core->vm_info, core, "\tRSP=%p, RBP=%p\n", (void *)core->vm_regs.rsp, (void *)core->vm_regs.rbp);
349
350     gla_rbp = get_addr_linear(core, core->vm_regs.rbp, &(core->segments.ss));
351
352
353     for (i = 0; i < 30; i++) {
354         addr_t hva_rbp = 0; 
355         addr_t hva_rip = 0; 
356         char * sym_name = NULL;
357         addr_t rip_val = 0;
358
359         if (core->mem_mode == PHYSICAL_MEM) {
360             if (v3_gpa_to_hva(core, gla_rbp, &hva_rbp) == -1) {
361                 PrintError(core->vm_info, core, "Could not translate Stack address\n");
362                 return;
363             }
364         } else if (core->mem_mode == VIRTUAL_MEM) {
365             if (v3_gva_to_hva(core, gla_rbp, &hva_rbp) == -1) {
366                 PrintError(core->vm_info, core, "Could not translate Virtual Stack address\n");
367                 return;
368             }
369         }
370
371
372         hva_rip = hva_rbp + v3_get_addr_width(core);
373         
374         if (cpu_mode == REAL) {
375             rip_val = (addr_t)*(uint16_t *)hva_rip;
376         } else if (cpu_mode == LONG) {
377             rip_val = (addr_t)*(uint64_t *)hva_rip;
378         } else {
379             rip_val = (addr_t)*(uint32_t *)hva_rip;
380         }
381
382         if (system_map) {
383             char * tmp_ptr = system_map->data;
384             char * sym_ptr = NULL;
385             uint64_t file_offset = 0; 
386             uint64_t sym_offset = 0;
387
388             while (file_offset < system_map->size) {
389                 sym_offset = strtox(tmp_ptr, &tmp_ptr);
390
391                 tmp_ptr += 3; // pass over symbol type
392
393                 if (sym_offset > rip_val) {
394                     char * end_ptr = strchr(sym_ptr, '\n');
395
396                     if (end_ptr) {
397                         *end_ptr = 0; // null terminate symbol...
398                     }
399
400                     sym_name = sym_ptr;
401                     break;
402                 }
403
404                 sym_ptr = tmp_ptr;
405                 { 
406                     char * end_ptr2 = strchr(tmp_ptr, '\n');
407
408                     if (!end_ptr2) {
409                         tmp_ptr += strlen(tmp_ptr) + 1;
410                     } else {
411                         tmp_ptr = end_ptr2 + 1;
412                     }
413                 }
414             }
415         }
416
417         if (!sym_name) {
418             sym_name = "?";
419         }
420
421         if (cpu_mode == REAL) {
422             V3_Print(core->vm_info, core, "Next RBP=0x%.4x, RIP=0x%.4x (%s)\n", 
423                      *(uint16_t *)hva_rbp,*(uint16_t *)hva_rip, 
424                      sym_name);
425             
426             gla_rbp = *(uint16_t *)hva_rbp;
427         } else if (cpu_mode == LONG) {
428             V3_Print(core->vm_info, core, "Next RBP=%p, RIP=%p (%s)\n", 
429                      (void *)*(uint64_t *)hva_rbp, (void *)*(uint64_t *)hva_rip,
430                      sym_name);
431             gla_rbp = *(uint64_t *)hva_rbp;
432         } else {
433             V3_Print(core->vm_info, core, "Next RBP=0x%.8x, RIP=0x%.8x (%s)\n", 
434                      *(uint32_t *)hva_rbp, *(uint32_t *)hva_rip,
435                      sym_name);
436             gla_rbp = *(uint32_t *)hva_rbp;
437         }
438
439     }
440 }
441
442
443 #ifdef __V3_32BIT__
444
445 void v3_print_GPRs(struct guest_info * core) {
446     struct v3_gprs * regs = &(core->vm_regs);
447     int i = 0;
448     v3_reg_t * reg_ptr;
449     char * reg_names[] = { "RDI", "RSI", "RBP", "RSP", "RBX", "RDX", "RCX", "RAX", NULL};
450
451     reg_ptr = (v3_reg_t *)regs;
452
453     V3_Print(info->vm_info, info, "32 bit GPRs:\n");
454
455     for (i = 0; reg_names[i] != NULL; i++) {
456         V3_Print(info->vm_info, info, "\t%s=0x%p (at %p)\n", reg_names[i], (void *)(addr_t)reg_ptr[i], &(reg_ptr[i]));  
457     }
458 }
459
460 void v3_print_idt(struct guest_info * core, addr_t idtr_base) {
461     addr_t base_hva;
462
463     if (core->mem_mode == PHYSICAL_MEM) {
464         v3_gpa_to_hva(core, 
465                       get_addr_linear(core, idtr_base, &(core->segments.cs)),
466                       &base_hva);
467         PrintError(core->vm_info, core, "Kind of weird that we got here.... physical mem?\n");
468     } else if (core->mem_mode == VIRTUAL_MEM) {
469         v3_gva_to_hva(core, 
470                       get_addr_linear(core, idtr_base, &(core->segments.cs)),
471                       &base_hva);
472     }
473
474     // SANITY CHECK
475     if (idtr_base != get_addr_linear(core, idtr_base, &(core->segments.cs))) {
476         PrintError(core->vm_info, core, "idtr base address != linear translation, might be something funky with cs\n");
477     }
478
479     int i;
480     char *types[16] = {"  ILGL","aTSS16","   LDT","bTSS16","call16","  task","intr16","trap16",
481         "  ILGL","aTSS32","  ILGL","bTSS32","call32","  ILGL","intr32","trap32"};
482
483     struct int_trap_gate_lgcy * entry;
484     entry = (struct int_trap_gate_lgcy *)base_hva;
485     PrintDebug(core->vm_info, core, "= IDT ========\n");
486     PrintDebug(core->vm_info, core, "  # | hex | selector | si:ti:rpl |   offset | type | dpl | s | p\n");
487     for (i = 0; i < NUM_IDT_ENTRIES; i++) {
488         uint32_t tmp = entry->selector;
489         struct segment_selector * seg = (struct segment_selector *)(&tmp);
490         PrintDebug(core->vm_info, core, "%3d | %3x |     %04x |   %03x:%x:%x | %04x%04x | %s |   %x | %x | %x | %x\n", i, i,
491                 entry->selector,
492                 seg->index, seg->ti, seg->rpl,
493                 entry->offset_hi, entry->offset_lo,
494                 types[entry->type], entry->dpl, entry->s, entry->p);
495         entry++;
496     }
497 }
498
499 void v3_print_gdt(struct guest_info * core, addr_t gdtr_base) {
500     addr_t base_hva;
501
502     if (core->mem_mode == PHYSICAL_MEM) {
503         v3_gpa_to_hva(core, 
504                       get_addr_linear(core, gdtr_base, &(core->segments.cs)),
505                       &base_hva);
506         PrintError(core->vm_info, core, "Kind of weird that we got here.... physical mem?\n");
507     } else if (core->mem_mode == VIRTUAL_MEM) {
508         v3_gva_to_hva(core, 
509                       get_addr_linear(core, gdtr_base, &(core->segments.cs)),
510                       &base_hva);
511     }
512
513     // SANITY CHECK
514     if (gdtr_base != get_addr_linear(core, gdtr_base, &(core->segments.cs))) {
515         PrintError(core->vm_info, core, "gdtr base address != linear translation, might be something funky with cs\n");
516     }
517
518     int i;
519     char* cd[2] = {"code","data"};
520     // TODO: handle possibility of gate/segment descriptor
521
522     struct code_desc_lgcy * entry;
523     entry = (struct code_desc_long *)base_hva;
524     PrintDebug(core->vm_info, core, "= GDT ========\n");
525     PrintDebug(core->vm_info, core, "  # | hex | limit |     base |  c/d | dpl | p\n");
526     for (i = 0; i < NUM_GDT_ENTRIES; i++) {
527         PrintDebug(core->vm_info, core, "%3d | %3x | %x%04x | %02x%02x%04x | %s |   %x | %x\n", i, i,
528                 entry->limit_hi, entry->limit_lo,
529                 entry->base_hi, entry->base_mid, entry->base_lo,
530                 cd[entry->one1], entry->dpl, entry->p);
531         entry++;
532     }
533 }
534
535 void v3_print_gp_error(struct guest_info * core, addr_t exit_info1) {
536     struct selector_error_code * error = (struct selector_error_code *)(&exit_info1);
537
538     PrintDebug(core->vm_info, core, "      selector index: %x, TI: %x, IDT: %x, EXT: %x (error=%llx)\n",
539             error->index, error->ti, error->idt, error->ext,
540             (unsigned long long)exit_info1);
541 }
542
543 #elif __V3_64BIT__
544
545 void v3_print_GPRs(struct guest_info * core) {
546     struct v3_gprs * regs = &(core->vm_regs);
547     int i = 0;
548     v3_reg_t * reg_ptr;
549     char * reg_names[] = { "RDI", "RSI", "RBP", "RSP", "RBX", "RDX", "RCX", "RAX", \
550                            "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15", NULL};
551
552     reg_ptr = (v3_reg_t *)regs;
553
554     V3_Print(core->vm_info, core, "64 bit GPRs:\n");
555
556     for (i = 0; reg_names[i] != NULL; i++) {
557         V3_Print(core->vm_info, core, "\t%s=0x%p (at %p)\n", reg_names[i], (void *)(addr_t)reg_ptr[i], &(reg_ptr[i]));  
558     }
559 }
560
561 void v3_print_idt(struct guest_info * core, addr_t idtr_base) {
562     addr_t base_hva;
563
564     if (core->mem_mode == PHYSICAL_MEM) {
565         v3_gpa_to_hva(core, 
566                       get_addr_linear(core, idtr_base, &(core->segments.cs)),
567                       &base_hva);
568     } else if (core->mem_mode == VIRTUAL_MEM) {
569         v3_gva_to_hva(core, 
570                       get_addr_linear(core, idtr_base, &(core->segments.cs)),
571                       &base_hva);
572     }
573
574     // SANITY CHECK
575     if (idtr_base != get_addr_linear(core, idtr_base, &(core->segments.cs))) {
576         PrintError(core->vm_info, core, "idtr base address != linear translation, might be something funky with cs\n");
577     }
578
579     int i;
580     char *types[16] = {"ILGL","ILGL"," LDT","ILGL","ILGL","ILGL","ILGL","ILGL","ILGL",
581         "aTSS","ILGL","bTSS","call","ILGL","intr","trap"};
582
583     struct int_trap_gate_long * entry;
584     entry = (struct int_trap_gate_long *)base_hva;
585     PrintDebug(core->vm_info, core, "= IDT ========\n");
586     PrintDebug(core->vm_info, core, "  # | hex | selector | si:ti:rpl |           offset | type | dpl | s | r | p\n");
587     for (i = 0; i < NUM_IDT_ENTRIES; i++) {
588         uint32_t tmp = entry->selector;
589         struct segment_selector * seg = (struct segment_selector *)(&tmp);
590         PrintDebug(core->vm_info, core, "%3d | %3x |     %04x |   %03x:%x:%x | %08x%04x%04x | %s |   %x | %x | %x | %x\n", i, i,
591                 entry->selector,
592                 seg->index, seg->ti, seg->rpl,
593                 entry->offset_hi, entry->offset_mid, entry->offset_lo,
594                 types[entry->type], entry->dpl, entry->s,
595                 entry->s, entry->p);
596         entry++;
597     }
598 }
599
600 void v3_print_gdt(struct guest_info * core, addr_t gdtr_base) {
601     addr_t base_hva;
602
603     if (core->mem_mode == PHYSICAL_MEM) {
604         v3_gpa_to_hva(core, 
605                       get_addr_linear(core, gdtr_base, &(core->segments.cs)),
606                       &base_hva);
607     } else if (core->mem_mode == VIRTUAL_MEM) {
608         v3_gva_to_hva(core, 
609                       get_addr_linear(core, gdtr_base, &(core->segments.cs)),
610                       &base_hva);
611     }
612
613     // SANITY CHECK
614     if (gdtr_base != get_addr_linear(core, gdtr_base, &(core->segments.cs))) {
615         PrintError(core->vm_info, core, "gdtr base address != linear translation, might be something funky with cs\n");
616     }
617
618     int i;
619     char* cd[2] = {"code","data"};
620     // TODO: handle possibility of gate/segment descriptor
621
622     struct code_desc_long * entry;
623     entry = (struct code_desc_long *)base_hva;
624     PrintDebug(core->vm_info, core, "= GDT ========\n");
625     PrintDebug(core->vm_info, core, "  # | hex | limit |     base |  c/d | dpl | p\n");
626     for (i = 0; i < NUM_GDT_ENTRIES; i++) {
627         PrintDebug(core->vm_info, core, "%3d | %3x | %x%04x | %02x%02x%04x | %s |   %x | %x\n", i, i,
628                 entry->limit_hi, entry->limit_lo,
629                 entry->base_hi, entry->base_mid, entry->base_lo,
630                 cd[entry->one1], entry->dpl, entry->p);
631         entry++;
632     }
633 }
634
635 void v3_print_gp_error(struct guest_info * core, addr_t exit_info1) {
636     struct selector_error_code * error = (struct selector_error_code *)(&exit_info1);
637
638     PrintDebug(core->vm_info, core, "      selector index: %x, TI: %x, IDT: %x, EXT: %x (error=%llx)\n",
639             error->index, error->ti, error->idt, error->ext,
640             (unsigned long long)exit_info1);
641 }
642
643 #endif