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.


addition of print macros for RTM code
[palacios.git] / palacios / src / extensions / ext_trans_mem.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) 2012, NWU EECS 441 Transactional Memory Team
11  * Copyright (c) 2012, The V3VEE Project <http://www.v3vee.org> 
12  * All rights reserved.
13  *
14  * Author:  Maciek Swiech <dotpyfe@u.northwestern.edu>
15  *          Kyle C. Hale <kh@u.northwestern.edu>
16  *          Marcel Flores <marcel-flores@u.northwestern.edu>
17  *          Zachary Bischof <zbischof@u.northwestern.edu>
18  *          
19  *
20  * This is free software.  You are permitted to use,
21  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
22  */
23
24 #include <palacios/vmm_mem.h>
25 #include <palacios/vmm.h>
26 #include <palacios/vmcb.h>
27 #include <palacios/vmm_decoder.h>
28 #include <palacios/vm_guest_mem.h>
29 #include <palacios/vmm_ctrl_regs.h>
30 #include <palacios/vmm_paging.h>
31 #include <palacios/vmm_direct_paging.h>
32 #include <palacios/svm.h>
33 #include <palacios/svm_handler.h>
34 #include <palacios/vmm_excp.h>
35 #include <palacios/vmm_extensions.h>
36 #include <palacios/vmm_sprintf.h>
37 #include <palacios/vmm_hashtable.h>
38
39 #include <extensions/trans_mem.h>
40 #include <extensions/tm_util.h>
41
42 #if !V3_CONFIG_DEBUG_TM_FUNC
43 #undef PrintDebug
44 #define PrintDebug(fmt, args...)
45 #endif
46
47 /* TODO LIST: 
48  * - save/restore register state on XBEGIN/XABORT
49  * - put status codes in RAX
50  * - Implement proper exceptions for failed XBEGINS etc.
51  */
52
53 /* this includes a mov to rax */
54 static const char * vmmcall_bytes = "\x48\xc7\xc0\x37\x13\x00\x00\x0f\x01\xd9"; 
55 static struct v3_tm_state * tm_global_state = NULL;
56
57
58 static void 
59 tm_translate_rip (struct guest_info * core, addr_t * target) 
60 {
61
62     if (core->mem_mode == PHYSICAL_MEM) {
63         v3_gpa_to_hva(core, 
64                 get_addr_linear(core, core->rip, &(core->segments.cs)), 
65                 target);
66     } else if (core->mem_mode == VIRTUAL_MEM) {
67         v3_gva_to_hva(core, 
68                 get_addr_linear(core, core->rip, &(core->segments.cs)), 
69                 target);
70     }
71
72 }
73
74
75 static void 
76 tm_read_instr (struct guest_info * core, 
77                            addr_t addr, 
78                            uchar_t * dst, 
79                            uint_t size) 
80 {
81
82     if (core->mem_mode == PHYSICAL_MEM) {
83         v3_read_gpa_memory(core, 
84                 get_addr_linear(core, addr , &(core->segments.cs)), 
85                 size, 
86                 dst);
87
88     } else { 
89        v3_read_gva_memory(core, 
90                 get_addr_linear(core, addr, &(core->segments.cs)), 
91                 size, 
92                 dst);
93     }
94
95 }
96
97
98 static int 
99 tm_handle_decode_fail (struct guest_info * core) 
100 {
101     addr_t cur_rip;
102     uint_t core_num;
103
104     tm_translate_rip(core, &cur_rip);
105
106 #ifdef V3_CONFIG_DEBUG_TM_FUNC
107     v3_dump_mem((uint8_t *)cur_rip, INSTR_BUF_SZ);
108 #endif
109
110     /* If we can't decode an instruction, we treat it as a catastrophic event, aborting *everyone* */
111     for (core_num = 0; core_num < core->vm_info->num_cores; core_num++ ) {
112         struct v3_trans_mem * remote_tm;
113
114         /* skip local core */
115         if (core_num == core->vcpu_id) {
116             continue;
117         }
118         
119         remote_tm = v3_get_ext_core_state(&(core->vm_info->cores[core_num]), "trans_mem");
120         if (!remote_tm) {
121             TM_ERR(core,DECODE,"couldnt get remote_tm\n");
122             return -1;
123         }
124
125         /* skip cores who aren't in transacitonal context */
126         if (remote_tm->TM_MODE == TM_OFF) {
127             continue;
128         }
129
130         TM_DBG(core,DECODE,"setting abort for core %d due to decoding error\n", core_num);
131         remote_tm->TM_ABORT = 1;
132     }
133
134     return 0;
135 }
136                                   
137
138 /* special casing for control-flow instructions
139  * returns 1 if we need to jump 
140  * returns -1 on error
141  */
142 static int 
143 tm_handle_ctrl_flow (struct guest_info * core,
144                                  struct v3_trans_mem * tm,
145                                  addr_t * instr_location,
146                                  struct x86_instr * struct_instr)
147
148 {
149     /* special casing for control flow instructions */
150     struct rflags * flags = (struct rflags *)&(core->ctrl_regs.rflags);
151     addr_t offset;
152     int to_jmp = 0;
153
154     switch (struct_instr->op_type) {
155
156         case V3_OP_JLE:
157             TM_DBG(core,DECODE, "!!++ JLE\n");
158             to_jmp = (flags->zf || flags->sf != flags->of);
159             offset = struct_instr->dst_operand.operand;
160
161             *instr_location = core->rip + tm->cur_instr_len + (to_jmp ? offset : 0);
162             tm->offset = offset;
163             tm->to_branch = to_jmp;
164             break;
165         case V3_OP_JAE:
166             TM_DBG(core,DECODE,"!!++ JAE\n");
167             to_jmp = (flags->cf == 0);
168             offset = struct_instr->dst_operand.operand;
169
170             *instr_location = core->rip + tm->cur_instr_len + (to_jmp ? offset : 0);
171             tm->offset = offset;
172             tm->to_branch = to_jmp;
173             break;
174         case V3_OP_JMP:
175             TM_DBG(core,DECODE,"!!++ JMP\n");
176             to_jmp = 1;
177             offset = struct_instr->dst_operand.operand;
178
179             *instr_location = core->rip + tm->cur_instr_len + (to_jmp ? offset : 0);
180             tm->offset = offset;
181             tm->to_branch = to_jmp;
182             break;
183         case V3_OP_JNZ:
184             TM_DBG(core,DECODE,"!!++ JNZ\n");
185             to_jmp = (flags->zf == 0);
186             offset = struct_instr->dst_operand.operand;
187
188             *instr_location = core->rip + tm->cur_instr_len + (to_jmp ? offset : 0);
189             tm->offset = offset;
190             tm->to_branch = to_jmp;
191             break;
192         case V3_OP_JL:
193             TM_DBG(core,DECODE,"!!++ JL\n");
194             to_jmp = (flags->sf != flags->of);
195             offset = struct_instr->dst_operand.operand;
196
197             *instr_location = core->rip + tm->cur_instr_len + (to_jmp ? offset : 0);
198             tm->offset = offset;
199             tm->to_branch = to_jmp;
200             break;
201         case V3_OP_JNS:
202             TM_DBG(core,DECODE,"!!++ JNS\n");
203             to_jmp = (flags->sf == 0);
204             offset = struct_instr->dst_operand.operand;
205
206             *instr_location = core->rip + tm->cur_instr_len + (to_jmp ? offset : 0);
207             tm->offset = offset;
208             tm->to_branch = to_jmp;
209             break;
210         default:
211             *instr_location = core->rip + tm->cur_instr_len;
212             break;
213     }
214     return to_jmp;
215 }
216
217
218 /* entry points :
219  *
220  * called inside #UD and VMMCALL handlers
221  * only affects global state in case of quix86 fall over
222  *  -> set other cores TM_ABORT to 1, return -2
223  */
224 static int 
225 v3_store_next_instr (struct guest_info * core, struct v3_trans_mem * tm) 
226 {
227     struct x86_instr struct_instr;
228     uchar_t cur_instr[INSTR_BUF_SZ];
229     addr_t  instr_location;
230
231     // Fetch the current instruction
232     tm_read_instr(core, core->rip, cur_instr, INSTR_BUF_SZ);
233
234     TM_DBG(core,STORE,"storing next instruction, current rip: %llx\n", (uint64_t)core->rip);
235
236     /* Attempt to decode current instruction to determine its length */
237     if (v3_decode(core, (addr_t)cur_instr, &struct_instr) == ERR_DECODE_FAIL) {
238         
239         TM_ERR(core,Error,"Could not decode currrent instruction (at %llx)\n", (uint64_t)core->rip);
240
241         /* this will attempt to abort all the remote cores */
242         if (tm_handle_decode_fail(core) == -1) {
243             TM_ERR(core,Error,"Could not handle failed decode\n");
244             return -1;
245         }
246
247         /* we need to trigger a local abort */
248         return ERR_STORE_MUST_ABORT;
249     }
250
251
252     /* we can't currently handle REP prefixes, abort */
253     if (struct_instr.op_type != V3_INVALID_OP &&
254             (struct_instr.prefixes.repne ||
255              struct_instr.prefixes.repnz ||
256              struct_instr.prefixes.rep   ||
257              struct_instr.prefixes.repe  ||
258              struct_instr.prefixes.repz)) {
259
260         TM_ERR(core,DECODE,"Encountered REP prefix, aborting\n");
261         return ERR_STORE_MUST_ABORT;
262     }
263
264     tm->cur_instr_len = struct_instr.instr_length;
265
266     /* handle jump instructions */
267     tm_handle_ctrl_flow(core, tm, &instr_location, &struct_instr);
268
269     /* save next 10 bytes after current instruction, we'll put vmmcall here */
270     tm_read_instr(core, instr_location, cur_instr, INSTR_INJECT_LEN);
271
272     /* store the next instruction and its length in info */
273     memcpy(tm->dirty_instr, cur_instr, INSTR_INJECT_LEN);
274
275     return 0;
276 }
277
278
279 static int 
280 v3_overwrite_next_instr (struct guest_info * core, struct v3_trans_mem * tm) 
281 {
282     addr_t ptr;
283
284     // save rax
285     tm->clobbered_rax = (core->vm_regs).rax;
286
287     ptr = core->rip;
288
289     /* we can't currently handle instructions that span page boundaries */
290     if ((ptr + tm->cur_instr_len) % PAGE_SIZE_4KB < (ptr % PAGE_SIZE_4KB)) {
291         TM_ERR(core,OVERWRITE,"emulated instr straddling page boundary\n");
292         return -1;
293     }
294
295     ptr = core->rip + tm->cur_instr_len + (tm->to_branch ? tm->offset : 0);
296
297     if ((ptr + INSTR_INJECT_LEN) % PAGE_SIZE_4KB < (ptr % PAGE_SIZE_4KB)) {
298         TM_ERR(core,OVERWRITE,"injected instr straddling page boundary\n");
299         return -1;
300     }
301
302     if (v3_gva_to_hva(core,
303                 get_addr_linear(core, ptr, &(core->segments.cs)),
304                 &ptr) == -1) {
305
306         TM_ERR(core,Error,"Calculating next rip hva failed\n");
307         return -1;
308     }
309
310     TM_DBG(core,REPLACE,"Replacing next instruction at addr %llx with vmm hyper call, len=%d\n",
311             core->rip + tm->cur_instr_len + (tm->to_branch ? tm->offset : 0), (int)tm->cur_instr_len );
312
313     /* Copy VMM call into the memory address of beginning of next instruction (ptr) */
314     memcpy((char*)ptr, vmmcall_bytes, INSTR_INJECT_LEN);
315
316     /* KCH: flag that we've dirtied an instruction, and store its host address */
317     tm->dirty_instr_flag = 1;
318     tm->dirty_gva        = core->rip + tm->cur_instr_len + (tm->to_branch ? tm->offset : 0);
319     tm->dirty_hva        = ptr;
320     tm->to_branch        = 0;
321
322     return 0;
323 }
324
325
326 /* entry points:
327  *
328  * this should only be called if TM_STATE == TM_NULL, additionally we check if our dirtied flag if set
329  */
330 int 
331 v3_restore_dirty_instr (struct guest_info * core) 
332 {
333     struct v3_trans_mem * tm = (struct v3_trans_mem *)v3_get_ext_core_state(core, "trans_mem");
334
335     /* Restore next instruction, transition to IFETCH state */
336     TM_DBG(core,RESTORE,"Restoring next instruction.\n");
337
338     /* check if we've actually done an instruction overwrite */
339     if (!(tm->dirty_instr_flag)) {
340         TM_DBG(core,RESTORE,"nothing to restore here...\n");
341         return 0;
342     }
343
344     // Actually restore instruction
345     memcpy((char*)tm->dirty_hva, tm->dirty_instr, INSTR_INJECT_LEN);
346
347     // Put rax back
348     (core->vm_regs).rax = tm->clobbered_rax; 
349
350     // Scoot rip back up
351     TM_DBG(core,RESTORE,"RIP in vmmcall: %llx\n", core->rip);
352     core->rip = tm->dirty_gva;
353
354     // clean up
355     tm->dirty_instr_flag = 0;
356     tm->dirty_gva = 0;
357     tm->dirty_hva = 0;
358     memset(tm->dirty_instr, 0, 15);
359
360     TM_DBG(core,RESTORE,"RIP after scooting it back up: %llx\n", core->rip);
361
362     return 0;
363 }
364
365
366 static addr_t 
367 tm_handle_fault_ifetch (struct guest_info * core, 
368                         struct v3_trans_mem * tm)
369 {
370     int sto;
371
372     TM_DBG(core,IFETCH,"Page fault caused by IFETCH: rip is the same as faulting address, we must be at an ifetch.\n");
373
374     sto = v3_store_next_instr(core, tm);
375
376     if (sto == ERR_STORE_FAIL) {
377         TM_ERR(core,EXIT,"Could not store next instruction in transaction\n");
378         return ERR_TRANS_FAULT_FAIL;
379     } else if (sto == ERR_STORE_MUST_ABORT) {
380         TM_DBG(core,EXIT,"aborting for some reason\n");
381         v3_handle_trans_abort(core);
382         return TRANS_FAULT_OK;
383     }
384
385     if (v3_overwrite_next_instr(core, tm) == -1) {
386         TM_ERR(core,PF,"problem overwriting instruction\n");
387         return ERR_TRANS_FAULT_FAIL;
388     }
389
390     tm->TM_STATE = TM_EXEC;
391
392     return TRANS_FAULT_OK;
393 }
394
395
396 static addr_t
397 tm_handle_fault_read (struct guest_info * core, 
398                       struct v3_trans_mem * tm,
399                       addr_t fault_addr,
400                       pf_error_t error)
401
402 {
403     // This page fault was caused by a read to memory in the current instruction for a core in TM mode
404     TM_DBG(core,DATA,"Page fault caused by read.\n");
405     TM_DBG(core,PF,"Adding %p to read list and hash\n", (void*)fault_addr);
406
407     if (add_mem_op_to_list(&(tm->trans_r_list), fault_addr) == -1) {
408         TM_ERR(core,PF,"problem adding to list\n");
409         return ERR_TRANS_FAULT_FAIL;
410     }
411
412     if (tm_record_access(tm, error.write, fault_addr) == -1) {
413         TM_ERR(core,PF,"problem recording access\n");
414         return ERR_TRANS_FAULT_FAIL;
415     }
416
417     /* if we have previously written to this address, we need to update our
418      * staging page and map it in */
419     if (list_contains_guest_addr(&(tm->trans_w_list), fault_addr)) {
420
421         TM_DBG(core,PF,"Saw a read from something in the write list\n");
422
423         /* write the value from linked list to staging page */
424         if (stage_entry(tm, &(tm->trans_w_list), fault_addr) == -1) {
425             TM_ERR(core,PF, "could not stage entry!\n");
426             return ERR_TRANS_FAULT_FAIL;
427         }
428
429         /* Hand it the staging page */
430         return (addr_t)(tm->staging_page);                
431
432     } else {
433
434         //Add it to the read set
435         addr_t shadow_addr = 0;
436
437         TM_DBG(core,PF,"Saw a read from a fresh address\n");
438
439         if (v3_gva_to_hva(core, (uint64_t)fault_addr, &shadow_addr) == -1) {
440             TM_ERR(core,PF,"Could not translate gva to hva for transaction read\n");
441             return ERR_TRANS_FAULT_FAIL;
442         }
443
444     }
445
446     return TRANS_FAULT_OK;
447 }
448
449
450 static addr_t
451 tm_handle_fault_write (struct guest_info * core,
452                        struct v3_trans_mem * tm,
453                        addr_t fault_addr,
454                        pf_error_t error)
455 {
456         void * data_loc;
457         addr_t virt_data_loc;
458         addr_t shadow_addr = 0;
459
460         TM_DBG(core,DATA,"Page fault cause by write\n");
461         TM_DBG(core,PF,"Adding %p to write list and hash\n", (void*)fault_addr);
462
463         if (add_mem_op_to_list(&(tm->trans_w_list), fault_addr) == -1) {
464             TM_ERR(core,WRITE,"could not add to list!\n");
465             return ERR_TRANS_FAULT_FAIL;
466         }
467
468         if (tm_record_access(tm, error.write, fault_addr) == -1) {
469             TM_ERR(core,WRITE,"could not record access!\n");
470             return ERR_TRANS_FAULT_FAIL;
471         }
472
473         if (v3_gva_to_hva(core, (uint64_t)fault_addr, &shadow_addr) == -1) {
474             TM_ERR(core,WRITE,"could not translate gva to hva for transaction read\n");
475             return ERR_TRANS_FAULT_FAIL;
476         }
477
478         // Copy existing values to the staging page, populating that field
479         // This avoids errors in optimized code such as ++, where the original
480         // value is not read, but simply incremented
481         data_loc = (void*)((addr_t)(tm->staging_page) + (shadow_addr % PAGE_SIZE_4KB)); 
482         
483         if (v3_hpa_to_hva((addr_t)(data_loc), &virt_data_loc) == -1) {
484             TM_ERR(core,WRITE,"Could not convert address on staging page to virt addr\n");
485             return ERR_TRANS_FAULT_FAIL;
486         }
487
488         TM_DBG(core,WRITE,"\tValue being copied (core %d): %p\n", core->vcpu_id, *((void**)(virt_data_loc)));
489         //memcpy((void*)virt_data_loc, (void*)shadow_addr, sizeof(uint64_t));
490         *(uint64_t*)virt_data_loc = *(uint64_t*)shadow_addr;
491
492         return (addr_t)(tm->staging_page);                
493 }
494
495
496 static addr_t
497 tm_handle_fault_extern_ifetch (struct guest_info * core,
498                                struct v3_trans_mem * tm,
499                                addr_t fault_addr,
500                                pf_error_t error)
501 {
502     int sto;
503
504     // system is in tm state, record the access
505     TM_DBG(core,IFETCH,"Page fault caused by IFETCH: we are not in TM, recording.\n");
506
507     sto = v3_store_next_instr(core,tm);
508
509     if (sto == ERR_STORE_FAIL) {
510         TM_ERR(core,Error,"Could not store next instruction in transaction\n");
511         return ERR_TRANS_FAULT_FAIL;
512
513     } else if (sto == ERR_STORE_MUST_ABORT) {
514         TM_ERR(core,IFETCH,"decode failed, going out of single stepping\n");
515         v3_handle_trans_abort(core);
516         return TRANS_FAULT_OK;
517     }
518
519     if (v3_overwrite_next_instr(core, tm) == -1) {
520         TM_ERR(core,IFETCH,"could not overwrite next instr!\n");
521         return ERR_TRANS_FAULT_FAIL;
522     }
523
524     tm->TM_STATE = TM_EXEC;
525
526     if (tm_record_access(tm, error.write, fault_addr) == -1) {
527         TM_ERR(core,IFETCH,"could not record access!\n");
528         return ERR_TRANS_FAULT_FAIL;
529     }
530
531     return TRANS_FAULT_OK;
532 }
533
534
535 static addr_t
536 tm_handle_fault_extern_access (struct guest_info * core,
537                                struct v3_trans_mem * tm,
538                                addr_t fault_addr,
539                                pf_error_t error)
540 {
541     TM_DBG(core,PF_HANDLE,"recording access\n");
542     if (tm_record_access(tm, error.write, fault_addr) == -1) {
543         TM_ERR(core,PF_HANDLE,"could not record access!\n");
544         return ERR_TRANS_FAULT_FAIL;
545     }
546
547     return TRANS_FAULT_OK;
548 }
549
550
551 static addr_t
552 tm_handle_fault_tmoff (struct guest_info * core)
553 {
554     TM_DBG(core,PF_HANDLE, "in pf handler but noone is in tm mode anymore (core %d), i should try to eliminate hypercalls\n", core->vcpu_id);
555
556     if (v3_restore_dirty_instr(core) == -1) {
557         TM_ERR(core,PF_HANDLE,"could not restore dirty instr!\n");
558         return ERR_TRANS_FAULT_FAIL;
559     }
560
561     return TRANS_FAULT_OK;
562 }
563
564
565 /* entry points:
566  *
567  * called from MMU - should mean at least tms->TM_MODE is on
568  *
569  * tm->on : ifetch -> store instr, overwrite instr
570  *          r/w    -> record hash, write log, store instr, overwrite instr
571  * tm->off: ifetch -> store instr, overwrite instr
572  *          r/w    -> record hash, store instr, overwrite instr
573  *
574  *          returns ERR_TRANS_FAULT_FAIL on error
575  *          TRANS_FAULT_OK when things are fine
576  *          addr when we're passing back a staging page
577  *
578  */
579 addr_t 
580 v3_handle_trans_mem_fault (struct guest_info * core, 
581                                   addr_t fault_addr, 
582                                   pf_error_t error) 
583 {
584     struct v3_trans_mem * tm = (struct v3_trans_mem *)v3_get_ext_core_state(core, "trans_mem");
585     struct v3_tm_state * tms = (struct v3_tm_state *)v3_get_extension_state(core->vm_info, "trans_mem");
586
587     if (!tm) {
588         TM_ERR(core,ERROR,": coudln't get core state\n");
589         return ERR_TRANS_FAULT_FAIL;
590     }
591
592     if (!tms) {
593         TM_ERR(core,ERROR,": couldn't get vm trans_mem state\n");
594         return ERR_TRANS_FAULT_FAIL;
595     }
596
597     TM_DBG(core,PF,"PF handler core->mode : %d, system->mode : %d\n", tm->TM_MODE, tms->TM_MODE);
598
599     if ((tm->TM_MODE == TM_ON) && 
600         ((void *)fault_addr == (void *)(core->rip))) {
601
602         return tm_handle_fault_ifetch(core, tm);
603
604     } else if ((tm->TM_MODE == TM_ON)    && 
605                (tm->TM_STATE == TM_EXEC) && 
606                (error.write == 0)) {
607
608         return tm_handle_fault_read(core, tm, fault_addr, error);
609
610     } else if ((tm->TM_MODE == TM_ON)    && 
611                (tm->TM_STATE == TM_EXEC) && 
612                (error.write == 1)) {
613
614         return tm_handle_fault_write(core, tm, fault_addr, error);
615
616
617     } else if ((tms->TM_MODE == TM_ON) &&
618               ((void *)fault_addr == (void *)(core->rip))) {
619
620         return tm_handle_fault_extern_ifetch(core, tm, fault_addr, error);
621
622     } else if ((tms->TM_MODE == TM_ON) && 
623                (tm->TM_STATE == TM_EXEC)) {
624
625         return tm_handle_fault_extern_access(core, tm, fault_addr, error);
626     } else {
627
628         return tm_handle_fault_tmoff(core);
629
630     }
631
632     return TRANS_FAULT_OK;
633 }
634
635
636 static int 
637 tm_handle_hcall_tmoff (struct guest_info * core, struct v3_trans_mem * tm)
638 {
639     if (tm->TM_MODE == TM_ON) {
640         TM_ERR(core,EXIT,"we are in tm mode but system is not!\n");
641         return TRANS_HCALL_FAIL;
642     }
643
644     // we got to an exit when things were off!
645     TM_DBG(core,EXIT,"system is off, restore the instruction and go away\n");
646
647     if (v3_restore_dirty_instr(core) == -1) {
648         TM_ERR(core,HCALL,"could not restore dirty instr!\n");
649         return TRANS_HCALL_FAIL;
650     }
651
652     tm->TM_STATE = TM_NULL;
653
654     return TRANS_HCALL_OK;
655 }
656
657
658 static int
659 tm_handle_hcall_dec_abort (struct guest_info * core, 
660                            struct v3_trans_mem * tm)
661 {
662     // only ever get here from TM DECODE
663     TM_DBG(core,EXIT,"we are in ABORT, call the abort handler\n");
664     tm->TM_ABORT = 0;
665
666     v3_handle_trans_abort(core);
667
668     TM_DBG(core,EXIT,"RIP after abort: %p\n", ((void*)(core->rip)));
669
670     return TRANS_HCALL_OK;
671 }
672
673
674 static int
675 tm_handle_hcall_ifetch_start (struct guest_info * core, 
676                               struct v3_trans_mem * tm)
677 {
678     tm->TM_STATE = TM_IFETCH;
679
680     TM_DBG(core,EXIT,"VMEXIT after TM_EXEC, blast away VTLB and go into TM_IFETCH\n");
681
682     // Finally, invalidate the shadow page table 
683     v3_invalidate_shadow_pts(core);
684
685     return TRANS_HCALL_OK;
686 }
687
688
689 static int 
690 tm_check_list_conflict (struct guest_info * core,
691                         struct v3_trans_mem * tm,
692                         struct list_head * access_list,
693                         v3_tm_op_t op_type)
694 {
695     struct mem_op * curr = NULL;
696     struct mem_op * tmp  = NULL;
697     int conflict = 0;
698
699     list_for_each_entry_safe(curr, tmp, access_list, op_node) {
700
701         conflict = tm_check_conflict(tm->ginfo->vm_info, curr->guest_addr, op_type, core->vcpu_id, tm->t_num);
702
703         if (conflict == ERR_CHECK_FAIL) {
704
705             TM_ERR(core,EXIT,"error checking for conflicts\n");
706             return TRANS_HCALL_FAIL;
707
708         } else if (conflict == CHECK_IS_CONFLICT) {
709
710             TM_DBG(core,EXIT,"we have a conflict, aborting\n");
711             v3_handle_trans_abort(core);
712             return CHECK_MUST_ABORT;
713
714         }
715
716     }
717
718     return TRANS_HCALL_OK;
719 }
720
721
722 static int 
723 tm_handle_hcall_check_conflicts (struct guest_info * core,
724                                  struct v3_trans_mem * tm)
725 {
726     int ret;
727
728     TM_DBG(core,EXIT,"still TM_ON\n");
729     TM_DBG(core,EXIT,"checking for conflicts\n");
730
731     if ((ret = tm_check_list_conflict(core, tm, &(tm->trans_w_list), OP_TYPE_WRITE)) == TRANS_HCALL_FAIL) {
732         return TRANS_HCALL_FAIL;
733     } else if (ret == CHECK_MUST_ABORT) {
734         return TRANS_HCALL_OK;
735     }
736     
737     if ((ret = tm_check_list_conflict(core, tm, &(tm->trans_r_list), OP_TYPE_READ)) == TRANS_HCALL_FAIL) {
738         return TRANS_HCALL_FAIL;
739     } else if (ret == CHECK_MUST_ABORT) {
740         return TRANS_HCALL_OK;
741     }
742
743     tm->TM_STATE = TM_IFETCH;
744
745     return TRANS_HCALL_OK;
746 }
747
748
749 /* trans mem hypercall handler 
750  * entry points:
751  *
752  * running mime (tm or tms on)
753  *   update record log
754  *   restore instr
755  *   overwrite instr
756  *   check for conflicts
757  *   flush vtlb
758  * abort (due to quix86)
759  *   restore instr
760  *   set all to abort
761  */
762 static int 
763 tm_handle_hcall (struct guest_info * core, 
764                  unsigned int hcall_id, 
765                  void * priv_data) 
766 {
767     struct v3_trans_mem * tm = (struct v3_trans_mem *)v3_get_ext_core_state(core, "trans_mem");
768     struct v3_tm_state * tms = (struct v3_tm_state *)v3_get_extension_state(core->vm_info, "trans_mem");
769
770     if (tms->TM_MODE == TM_OFF) {
771         return tm_handle_hcall_tmoff(core, tm);
772     }
773
774     // Previous instruction has finished, copy staging page back into linked list!
775     if (update_list(tm, &(tm->trans_w_list)) == -1) {
776         TM_ERR(core,HCALL,"could not update_list!\n");
777         return TRANS_HCALL_FAIL;
778     }
779
780     // Done handling previous instruction, must put back the next instruction, reset %rip and go back to IFETCH state
781     TM_DBG(core,EXIT,"saw VMEXIT, need to restore previous state and proceed\n");
782
783     if (v3_restore_dirty_instr(core) == -1) {
784         TM_ERR(core,HCALL,"could not restore dirty instr!\n");
785         return TRANS_HCALL_FAIL;
786     }
787     
788     /* Check TM_STATE */
789     if (tm->TM_ABORT == 1 && 
790         tms->TM_MODE == TM_ON) {
791
792         return tm_handle_hcall_dec_abort(core, tm);
793
794     } else if (tm->TM_STATE == TM_EXEC) {
795         return tm_handle_hcall_ifetch_start(core, tm);
796     }
797
798     /* Check TM_MODE */
799     if (tm->TM_MODE == TM_ON && 
800         tms->TM_MODE == TM_ON) {
801
802         return tm_handle_hcall_check_conflicts(core, tm);
803
804     } else if (tm->TM_MODE == TM_OFF) {
805         TM_DBG(core,EXIT,"we are in TM_OFF\n");
806     }
807
808     return TRANS_HCALL_OK;
809 }
810
811
812 int 
813 v3_tm_inc_tnum (struct v3_trans_mem * tm) 
814 {
815     addr_t irqstate;
816     uint64_t new_ctxt;
817     uint64_t * lt;
818
819     lt = tm_global_state->last_trans;
820
821     // grab global last_trans
822     irqstate = v3_lock_irqsave(tm_global_state->lock);
823     new_ctxt = ++(lt[tm->ginfo->vcpu_id]);
824     v3_unlock_irqrestore(tm_global_state->lock, irqstate);
825
826     tm->t_num++;
827     /*
828     TM_DBG(tm->ginfo,INC TNUM,"global state is |%d|%d|, my tnum is %d\n", (int)lt[0],
829                                                                         (int)lt[1], (int)tm->t_num);
830                                                                         */
831     if (new_ctxt != tm->t_num) {
832         TM_ERR(tm->ginfo,TM_INC_TNUM,"misaligned global and local context value\n");
833         return -1;
834     }
835
836     return 0;
837 }
838
839
840 int 
841 v3_handle_trans_abort (struct guest_info * core) 
842 {
843     struct v3_trans_mem * tm = (struct v3_trans_mem *)v3_get_ext_core_state(core, "trans_mem");
844
845     // Free the staging page
846     if (v3_free_staging_page(tm) == -1) {
847         TM_ERR(core,ABORT,"problem freeing staging page\n");
848         return -1;
849     }
850
851     // Clear the VTLB which still has our staging page in it
852     if (v3_clr_vtlb(core) == -1) {
853         TM_ERR(core,ABORT,"problem clearing vtlb\n");
854         return -1;
855     }
856
857     // Free the lists
858     v3_clear_tm_lists(tm);
859
860     TM_DBG(core,ABORT -- handler,"TM_MODE: %d | RIP: %llx | XABORT RIP: %llx\n", tm->TM_MODE, (uint64_t)core->rip, (uint64_t)tm->fail_call);
861
862     if (tm->TM_MODE == TM_ON) {
863         TM_DBG(core,ABORT,"Setting RIP to %llx\n", (uint64_t)tm->fail_call);
864         core->rip = tm->fail_call;
865
866         // Turn TM off
867         v3_clr_tm(tm);
868
869         // transaction # ++
870         v3_tm_inc_tnum(tm);
871     }
872     
873   
874     // time to garbage collect
875     if (tm_hash_gc(tm) == -1) {
876         TM_ERR(core,GC,"could not gc!\n");
877         return -1;
878     }
879
880     return 0;
881 }
882
883
884 static uint_t 
885 tm_hash_fn (addr_t key) 
886 {
887     return v3_hash_long(key, sizeof(void *));
888 }
889
890
891 static int 
892 tm_eq_fn (addr_t key1, addr_t key2) 
893 {
894     return (key1 == key2);
895 }
896
897
898 static uint_t 
899 tm_hash_buf_fn (addr_t key) 
900 {
901     return v3_hash_long(key, sizeof(addr_t));
902 }
903
904
905 static int 
906 tm_eq_buf_fn(addr_t key1, addr_t key2) 
907 {
908     return (key1 == key2);
909 }
910
911
912 /* this checks if the remote access was done on the same
913  * local transaction number as the current one */
914 static int 
915 tm_check_context (struct v3_vm_info * vm,
916                   addr_t gva,
917                   uint64_t core_num,
918                   uint64_t curr_ctxt,
919                   uint64_t * curr_lt,
920                   v3_tm_op_t op_type)
921 {
922     uint64_t  core_id_sub;
923     struct v3_tm_access_type * type = NULL;
924
925     for (core_id_sub = 0; core_id_sub < vm->num_cores; core_id_sub++) {
926         struct v3_trans_mem * remote_tm;
927         void * buf[3];
928         addr_t key;
929
930         /* skip the core that's doing the checking */
931         if (core_id_sub == core_num) {
932             continue;
933         }
934
935         remote_tm = v3_get_ext_core_state(&(vm->cores[core_id_sub]), "trans_mem");
936         if (!remote_tm) {
937             PrintError(vm, VCORE_NONE, "Could not get ext core state for core %llu\n", core_id_sub);
938             return ERR_CHECK_FAIL;
939         }
940
941         buf[0] = (void *)gva;
942         buf[1] = (void *)core_id_sub;
943         buf[2] = (void *)curr_lt[core_id_sub];
944
945         key = v3_hash_buffer((uchar_t*)buf, sizeof(void*)*3);
946
947         type = (struct v3_tm_access_type *)HTABLE_SEARCH(remote_tm->access_type, key);
948
949         if (type) {
950             // conflict!
951             if ( (op_type == OP_TYPE_WRITE && (type->w || type->r)) || // so basically if write?
952                     (op_type != OP_TYPE_WRITE && type->w)) {
953                 return CHECK_IS_CONFLICT;
954             }
955         }
956     }
957
958     return CHECK_NO_CONFLICT;
959 }
960
961
962 /* check all the contexts in the list for a conflict */
963 static int 
964 tm_check_all_contexts (struct v3_vm_info * vm,
965                        struct list_head * hash_list,
966                        addr_t   gva,
967                        v3_tm_op_t  op_type,
968                        uint64_t core_num, 
969                        uint64_t curr_ctxt) 
970 {
971     struct hash_chain * curr = NULL;
972     struct hash_chain * tmp  = NULL;
973     uint64_t * curr_lt       = NULL;
974     int ret = 0;
975
976     list_for_each_entry_safe(curr, tmp, hash_list, lt_node) {
977
978         curr_lt = curr->curr_lt;
979
980         if (curr_lt[core_num] == curr_ctxt) {
981
982             ret = tm_check_context(vm, gva, core_num, curr_ctxt, curr_lt, op_type);
983
984             if (ret == ERR_CHECK_FAIL) {
985                 return ERR_CHECK_FAIL;
986             } else if (ret == CHECK_IS_CONFLICT) {
987                 return CHECK_IS_CONFLICT;
988             }
989
990         }
991
992     }
993
994     return CHECK_NO_CONFLICT;
995 }
996
997
998 /* The following access patterns trigger an abort:
999  * We: Read     |   Anyone Else: Write
1000  * We: Write    |   Anyone Else: Read, Write
1001  *
1002  * (pg 8-2 of haswell manual)
1003  *
1004  * returns ERR_CHECK_FAIL on error
1005  * returns CHECK_IS_CONFLICT if there is a conflict
1006  * returns CHECK_NO_CONFLICT  if there isn't
1007  */
1008 int 
1009 tm_check_conflict (struct v3_vm_info * vm,
1010                    addr_t gva,
1011                    v3_tm_op_t op_type,
1012                    uint64_t core_num, 
1013                    uint64_t curr_ctxt) 
1014 {
1015     uint64_t core_id;
1016
1017     /* loop over other cores -> core_id */
1018     for (core_id = 0; core_id < vm->num_cores; core_id++) {
1019
1020         struct guest_info * core = NULL;
1021         struct v3_trans_mem * tm = NULL;
1022         struct list_head * hash_list;
1023
1024         /* only check other cores */
1025         if (core_id == core_num) {
1026             continue;
1027         }
1028
1029         core = &(vm->cores[core_id]);
1030         tm = (struct v3_trans_mem*)v3_get_ext_core_state(core, "trans_mem");
1031         
1032         if (!tm) {
1033             PrintError(vm, VCORE_NONE, "+++ TM ERROR +++ Couldn't get core state for core %llu\n", core_id);
1034             return ERR_CHECK_FAIL;
1035         }
1036
1037         /* this core didn't access the address, move on */
1038         if (!(hash_list = (struct list_head *)HTABLE_SEARCH(tm->addr_ctxt, gva))) {
1039             continue;
1040
1041         } else {
1042
1043             /* loop over chained hash for gva, find fields with curr_ctxt -> curr_lt*/
1044             int ret = tm_check_all_contexts(vm, hash_list, gva, op_type, core_num, curr_ctxt);
1045
1046             if (ret == ERR_CHECK_FAIL) {
1047                 return ERR_CHECK_FAIL;
1048             } else if (ret == CHECK_IS_CONFLICT) {
1049                 return CHECK_IS_CONFLICT;
1050             } 
1051
1052         }
1053     }
1054
1055     return CHECK_NO_CONFLICT;
1056 }
1057
1058
1059 static int 
1060 tm_need_to_gc (struct v3_trans_mem * tm,
1061                struct hash_chain * curr,
1062                uint64_t * lt_copy,
1063                uint64_t tmoff)
1064 {
1065     uint64_t to_gc = 1;
1066     uint64_t i;
1067
1068     /* if none of the cores are in transactional context, 
1069      * we know we can collect this context 
1070      */
1071     if (!tmoff) {
1072
1073         for (i = 0; i < tm->ginfo->vm_info->num_cores; i++) {
1074             /* if *any* of the cores are active in a transaction 
1075              * number that is current (listed in this context),
1076              * we know we can't collect this context, as it 
1077              * will be needed when that core's transaction ends
1078              */
1079             if (curr->curr_lt[i] >= lt_copy[i]) {
1080                 to_gc = 0;
1081                 break;
1082             }
1083         }
1084
1085     }
1086     return to_gc;
1087 }
1088
1089
1090 static void 
1091 tm_del_stale_ctxt (struct hash_chain * curr)
1092 {
1093         list_del(&(curr->lt_node));
1094         V3_Free(curr->curr_lt);
1095         V3_Free(curr);
1096 }
1097
1098
1099 static void 
1100 tm_del_acc_entry (struct v3_trans_mem * tm, addr_t key)
1101 {
1102     v3_htable_remove(tm->access_type, key, 0);
1103     (tm->access_type_entries)--;
1104 }
1105
1106
1107 static int 
1108 tm_collect_context (struct v3_trans_mem * tm, 
1109                     struct hashtable_iter * ctxt_iter, 
1110                     struct hash_chain * curr, 
1111                     uint64_t * begin_time,
1112                     uint64_t * end_time,
1113                     addr_t gva)
1114 {
1115         uint64_t i;
1116
1117         for (i = 0; i < tm->ginfo->vm_info->num_cores; i++) {
1118             void * buf[3];
1119             struct v3_tm_access_type * type;
1120             addr_t key;
1121
1122             rdtscll(*end_time);
1123             if ((*end_time - *begin_time) > 100000000) {
1124                 TM_ERR(tm->ginfo,GC,"time threshhold exceeded, exiting!!!\n");
1125                 return -1;
1126             }
1127             
1128             buf[0] = (void *)gva;
1129             buf[1] = (void *)i;
1130             buf[2] = (void *)curr->curr_lt[i];
1131
1132             key = v3_hash_buffer((uchar_t*)buf, sizeof(void*)*3);
1133
1134             type = (struct v3_tm_access_type *)v3_htable_search(tm->access_type, key);
1135
1136             if (!type) { // something has gone terribly wrong
1137                 TM_ERR(tm->ginfo,GC,"could not find accesstype entry to gc, THIS! IS! WRONG!\n");
1138                 return -1;
1139             }
1140
1141             /* delete the access type entry */
1142             tm_del_acc_entry(tm, key);
1143         }
1144
1145         /* delete the stale context */
1146         tm_del_stale_ctxt(curr);
1147
1148         return 0;
1149 }
1150
1151
1152 static int 
1153 tm_collect_all_contexts (struct v3_trans_mem * tm,
1154                          struct hashtable_iter * ctxt_iter,
1155                          uint64_t tmoff,
1156                          uint64_t * lt_copy,
1157                          uint64_t * begin_time,
1158                          uint64_t * end_time)
1159 {
1160     struct hash_chain * tmp;
1161     struct hash_chain * curr;
1162     struct list_head * chain_list;
1163     addr_t gva;
1164
1165     gva = (addr_t)v3_htable_get_iter_key(ctxt_iter);
1166     
1167     chain_list = (struct list_head *)v3_htable_get_iter_value(ctxt_iter);
1168
1169     /* this is a chained hash, so for each address, we will have
1170      * a list of contexts. We now check each context to see
1171      * whether or not it can be collected
1172      */
1173     list_for_each_entry_safe(curr, tmp, chain_list, lt_node) {
1174
1175         uint64_t to_gc = tm_need_to_gc(tm, curr, lt_copy, tmoff);
1176
1177         /* not garbage, go on to the next context in the list */
1178         if (!to_gc) {
1179             TM_DBG(tm->ginfo,GC,"not garbage collecting entries for address %llx\n", (uint64_t)gva);
1180             continue;
1181         }
1182
1183         TM_DBG(tm->ginfo,GC,"garbage collecting entries for address %llx\n", (uint64_t)gva);
1184
1185         /* found one, delete corresponding entries in access_type */
1186         if (tm_collect_context(tm, ctxt_iter, curr, begin_time, end_time, gva) == -1) {
1187             TM_ERR(tm->ginfo,GC,"ERROR collecting context\n");
1188             return -1;
1189         }
1190
1191     }
1192
1193     /* if context list (hash chain) is now empty, remove the hash entry */
1194     if (list_empty(chain_list)) {
1195         v3_htable_iter_remove(ctxt_iter, 0);
1196         (tm->addr_ctxt_entries)--;
1197     } else {
1198         v3_htable_iter_advance(ctxt_iter);
1199     }
1200
1201     /* give the CPU away NONONO NEVER YIELD WHILE HOLDING A LOCK */
1202     //V3_Yield();
1203
1204     return 0;
1205 }
1206
1207
1208 int 
1209 tm_hash_gc (struct v3_trans_mem * tm) 
1210 {
1211     addr_t irqstate, irqstate2;
1212     int rc = 0;
1213     uint64_t begin_time, end_time, tmoff;
1214     uint64_t * lt_copy;
1215     struct v3_tm_state * tms = NULL;
1216     struct hashtable_iter * ctxt_iter = NULL;
1217
1218     tms = (struct v3_tm_state *)v3_get_extension_state(tm->ginfo->vm_info, "trans_mem");
1219     if (!tms) {
1220         TM_ERR(tm->ginfo,GC,"could not alloc tms\n");
1221         return -1;
1222     }
1223
1224     TM_DBG(tm->ginfo,GC,"beginning garbage collection\n");
1225     TM_DBG(tm->ginfo,GC,"\t %d entries in addr_ctxt (pre)\n", (int)v3_htable_count(tm->addr_ctxt));
1226     TM_DBG(tm->ginfo,GC,"\t %d entries in access_type (pre)\n", (int)v3_htable_count(tm->access_type));
1227
1228     tmoff = (tms->cores_active == 0);
1229
1230     lt_copy = V3_Malloc(sizeof(uint64_t)*(tm->ginfo->vm_info->num_cores));
1231     if (!lt_copy) {
1232         TM_ERR(tm->ginfo,GC,"Could not allocate space for lt_copy\n");
1233         return -1;
1234     }
1235
1236     memset(lt_copy, 0, sizeof(uint64_t)*(tm->ginfo->vm_info->num_cores));
1237
1238     rdtscll(begin_time);
1239
1240     /* lt_copy holds the last transaction number for each core */
1241     irqstate = v3_lock_irqsave(tm_global_state->lock);
1242     memcpy(lt_copy, tm_global_state->last_trans, sizeof(uint64_t)*(tm->ginfo->vm_info->num_cores));
1243     v3_unlock_irqrestore(tm_global_state->lock, irqstate);
1244
1245     /* lock both hashes */
1246     irqstate = v3_lock_irqsave(tm->addr_ctxt_lock);
1247     irqstate2 = v3_lock_irqsave(tm->access_type_lock);
1248
1249     /* loop over hash entries in addr_ctxt */
1250     ctxt_iter = v3_create_htable_iter(tm->addr_ctxt);
1251     if (!ctxt_iter) {
1252         TM_ERR(tm->ginfo,GC,"could not create htable iterator\n");
1253         rc = -1;
1254         goto out;
1255     }
1256
1257     /* we check each address stored in the hash */
1258     while (ctxt_iter->entry) {
1259         /* NOTE: this call advances the hash iterator */
1260         if (tm_collect_all_contexts(tm, ctxt_iter, tmoff, lt_copy, &begin_time, &end_time) == -1) {
1261             rc = -1;
1262             goto out1;
1263         }
1264     }
1265
1266 out1:
1267     v3_destroy_htable_iter(ctxt_iter);
1268 out:
1269     V3_Free(lt_copy);
1270     v3_unlock_irqrestore(tm->access_type_lock, irqstate);
1271     v3_unlock_irqrestore(tm->addr_ctxt_lock, irqstate2);
1272
1273     rdtscll(end_time);
1274
1275     if (rc == -1) {
1276         TM_ERR(tm->ginfo,GC,"garbage collection failed, time spent: %d cycles\n", (int)(end_time - begin_time));
1277     } else {
1278         TM_DBG(tm->ginfo,GC,"ended garbage collection succesfuly, time spent: %d cycles\n", (int)(end_time - begin_time));
1279     }
1280
1281     TM_DBG(tm->ginfo,GC,"\t %d entries in addr_ctxt (post)\n", (int)v3_htable_count(tm->addr_ctxt));
1282     TM_DBG(tm->ginfo,GC,"\t %d entries in access_type (post)\n", (int)v3_htable_count(tm->access_type));
1283
1284     return rc;
1285 }
1286     
1287
1288 /* TODO: break out the for loops in these functions */
1289 static int
1290 tm_update_ctxt_list (struct v3_trans_mem * tm, 
1291                      uint64_t * lt_copy,
1292                      addr_t gva,
1293                      uint8_t write,
1294                      struct list_head * hash_list)
1295 {
1296     struct hash_chain * curr = NULL;
1297     struct hash_chain * tmp  = NULL;
1298     uint64_t num_cores = tm->ginfo->vm_info->num_cores;
1299     uint64_t core_id;
1300     uint_t new_le = 1;
1301     uint_t new_e;
1302
1303     list_for_each_entry_safe(curr, tmp, hash_list, lt_node) {
1304         uint_t i;
1305         uint8_t same = 1;
1306
1307         for (i = 0; i < num_cores; i++) {
1308             if (curr->curr_lt[i] != lt_copy[i]) {
1309                 same = 0;
1310                 break;
1311             }
1312         }
1313
1314         if (same) {
1315             new_le = 0;
1316             break;
1317         }
1318
1319     }
1320
1321     if (new_le) {
1322         struct hash_chain * new_l = V3_Malloc(sizeof(struct hash_chain));
1323
1324         if (!new_l) {
1325             TM_ERR(tm->ginfo,HASH,"Could not allocate new list\n");
1326             return -1;
1327         }
1328
1329         memset(new_l, 0, sizeof(struct hash_chain));
1330
1331         new_l->curr_lt = lt_copy;
1332
1333         list_add_tail(&(new_l->lt_node), hash_list);
1334     }
1335
1336     for (core_id = 0; core_id < num_cores; core_id++) {
1337         struct v3_tm_access_type * type;
1338         struct v3_ctxt_tuple tup;
1339         tup.gva     = (void*)gva;
1340         tup.core_id = (void*)core_id;
1341         tup.core_lt = (void*)lt_copy[core_id];
1342         addr_t key;
1343
1344         key = v3_hash_buffer((uchar_t*)&tup, sizeof(struct v3_ctxt_tuple));
1345
1346         new_e = 0;
1347
1348         type = (struct v3_tm_access_type *)HTABLE_SEARCH(tm->access_type, key);
1349
1350         if (!type) {
1351             // no entry yet
1352             new_e = 1;
1353             type = V3_Malloc(sizeof(struct v3_tm_access_type));
1354
1355             if (!type) {
1356                 TM_ERR(tm->ginfo,HASH,"could not allocate type access struct\n");
1357                 return -1;
1358             }
1359         }
1360
1361         if (write) {
1362             type->w = 1;
1363         } else {
1364             type->r = 1;
1365         }
1366
1367         if (new_e) {
1368             if (HTABLE_INSERT(tm->access_type, key, type) == 0) {
1369                 TM_ERR(tm->ginfo,HASH,"problem inserting new mem access in htable\n");
1370                 return -1;
1371             }
1372             (tm->access_type_entries)++;
1373         }
1374     }
1375
1376     return 0;
1377 }
1378
1379
1380 /* no entry in addr-ctxt yet, create one */
1381 static int
1382 tm_create_ctxt_key (struct v3_trans_mem * tm,
1383                     uint64_t * lt_copy,
1384                     addr_t gva,
1385                     uint8_t write)
1386 {
1387     struct list_head * hash_list = NULL;
1388     struct hash_chain * new_l = NULL;
1389     uint64_t num_cores = tm->ginfo->vm_info->num_cores;
1390
1391     hash_list = (struct list_head *)V3_Malloc(sizeof(struct list_head));
1392
1393     if (!hash_list) {
1394         TM_ERR(tm->ginfo,HASH,"Problem allocating hash_list\n");
1395         return -1;
1396     }
1397
1398     INIT_LIST_HEAD(hash_list);
1399
1400     new_l = V3_Malloc(sizeof(struct hash_chain));
1401
1402     if (!new_l) {
1403         TM_ERR(tm->ginfo,HASH,"Problem allocating hash_chain\n");
1404         goto out_err;
1405     }
1406
1407     memset(new_l, 0, sizeof(struct hash_chain));
1408
1409     new_l->curr_lt = lt_copy;
1410
1411     /* add the context to the hash chain */
1412     list_add_tail(&(new_l->lt_node), hash_list);
1413
1414     if (!(HTABLE_INSERT(tm->addr_ctxt, gva, hash_list))) {
1415         TM_ERR(tm->ginfo,HASH CHAIN,"problem inserting new chain into hash\n");
1416         goto out_err1;
1417     }
1418
1419     (tm->addr_ctxt_entries)++;
1420
1421     uint64_t core_id;
1422     /* TODO: we need a way to unwind and deallocate for all cores on failure here */
1423     for (core_id = 0; core_id < num_cores; core_id++) {
1424         struct v3_tm_access_type * type = NULL;
1425         struct v3_ctxt_tuple tup;
1426         tup.gva     = (void*)gva;
1427         tup.core_id = (void*)core_id;
1428         tup.core_lt = (void*)lt_copy[core_id];
1429         addr_t key;
1430
1431         type = V3_Malloc(sizeof(struct v3_tm_access_type));
1432
1433         if (!type) {
1434             TM_ERR(tm->ginfo,HASH,"could not allocate access type struct\n");
1435             goto out_err1;
1436         }
1437
1438         if (write) {
1439             type->w = 1;
1440         } else {
1441             type->r = 1;
1442         }
1443
1444         key = v3_hash_buffer((uchar_t*)&tup, sizeof(struct v3_ctxt_tuple));
1445
1446         if (HTABLE_INSERT(tm->access_type, key, type) == 0) {
1447             TM_ERR(tm->ginfo,HASH,"TM: problem inserting new mem access in htable\n");
1448             goto out_err1;
1449         }
1450         (tm->access_type_entries)++;
1451     }
1452
1453     return 0;
1454
1455 out_err1:
1456     list_del(&(new_l->lt_node));
1457     V3_Free(new_l);
1458 out_err:
1459     V3_Free(hash_list);
1460     return -1;
1461 }
1462
1463
1464 /* entry points:
1465  *
1466  * called during MIME execution
1467  * record memory access in conflict logs
1468  *   this locks the table during insertion
1469  */
1470 int 
1471 tm_record_access (struct  v3_trans_mem * tm, 
1472                   uint8_t write,
1473                   addr_t  gva) 
1474 {
1475     uint64_t * lt_copy;
1476     struct list_head * hash_list;
1477     addr_t irqstate;
1478     uint64_t num_cores;
1479
1480     num_cores = tm->ginfo->vm_info->num_cores;
1481
1482     TM_DBG(tm->ginfo,REC,"recording addr %llx, addr-ctxt.cnt = %d, access-type.cnt = %d\n", (uint64_t)gva,
1483                                         (int)v3_htable_count(tm->addr_ctxt), (int)v3_htable_count(tm->access_type));
1484     //PrintDebug(tm->ginfo->vm_info, tm->ginfo,"\tWe think that addr-ctxt.cnt = %d, access-type.cnt = %d\n",(int)tm->addr_ctxt_entries,(int)tm->access_type_entries);
1485
1486     lt_copy = V3_Malloc(sizeof(uint64_t)*num_cores);
1487     if (!lt_copy) {
1488         TM_ERR(tm->ginfo,REC,"Allocating array failed\n");
1489         return -1;
1490     }
1491
1492     memset(lt_copy, 0, sizeof(uint64_t)*num_cores);
1493
1494     irqstate = v3_lock_irqsave(tm_global_state->lock);
1495     memcpy(lt_copy, tm_global_state->last_trans, sizeof(uint64_t)*num_cores);
1496     v3_unlock_irqrestore(tm_global_state->lock, irqstate);
1497
1498     if (!(hash_list = (struct list_head *)HTABLE_SEARCH(tm->addr_ctxt, gva))) {
1499         /* we haven't created a context list for this address yet, go do it */
1500         return tm_create_ctxt_key(tm, lt_copy, gva, write);
1501
1502     } else {
1503         /* we have a context list for this addres already, do we need to create a new context? */
1504         return tm_update_ctxt_list(tm, lt_copy, gva, write, hash_list);
1505     }
1506
1507     return 0;
1508 }
1509
1510
1511 static int 
1512 init_trans_mem (struct v3_vm_info * vm, 
1513                 v3_cfg_tree_t * cfg, 
1514                 void ** priv_data) 
1515 {
1516     struct v3_tm_state * tms; 
1517      
1518     PrintDebug(vm, VCORE_NONE, "Trans Mem. Init\n");
1519
1520     tms = V3_Malloc(sizeof(struct v3_tm_state));
1521     if (!tms) {
1522         PrintError(vm, VCORE_NONE, "Problem allocating v3_tm_state\n");
1523         return -1;
1524     }
1525
1526     memset(tms, 0, sizeof(struct v3_tm_state));
1527
1528     if (v3_register_hypercall(vm, TM_KICKBACK_CALL, tm_handle_hcall, NULL) == -1) {
1529       PrintError(vm, VCORE_NONE, "TM could not register hypercall\n");
1530       goto out_err;
1531     }
1532
1533     v3_lock_init(&(tms->lock));
1534
1535     tms->TM_MODE      = TM_OFF;
1536     tms->cores_active = 0;
1537
1538     uint64_t * lt = V3_Malloc(sizeof(uint64_t) * vm->num_cores);
1539     if (!lt) {
1540         PrintError(vm, VCORE_NONE, "Problem allocating last_trans array\n");
1541         goto out_err1;
1542     }
1543
1544     memset(lt, 0, sizeof(uint64_t) * vm->num_cores);
1545
1546     int i;
1547     for (i = 0; i < vm->num_cores; i++) {
1548         lt[i] = 0;
1549     }
1550
1551     tms->last_trans = lt;
1552
1553     *priv_data = tms;
1554     tm_global_state = tms;
1555
1556     return 0;
1557
1558 out_err1:
1559     v3_lock_deinit(&(tms->lock));
1560     v3_remove_hypercall(vm, TM_KICKBACK_CALL);
1561 out_err:
1562     V3_Free(tms);
1563     return -1;
1564 }
1565
1566
1567 static int 
1568 init_trans_mem_core (struct guest_info * core, 
1569                      void * priv_data, 
1570                      void ** core_data) 
1571 {
1572     struct v3_trans_mem * tm = V3_Malloc(sizeof(struct v3_trans_mem));
1573  
1574     TM_DBG(core,INIT, "Trans Mem. Core Init\n");
1575
1576     if (!tm) {
1577         TM_ERR(core,INIT, "Problem allocating TM state\n");
1578         return -1;
1579     }
1580
1581     memset(tm, 0, sizeof(struct v3_trans_mem));
1582
1583     INIT_LIST_HEAD(&tm->trans_r_list);
1584     INIT_LIST_HEAD(&tm->trans_w_list);
1585
1586     tm->addr_ctxt  = v3_create_htable(0, tm_hash_fn, tm_eq_fn);
1587     if (!(tm->addr_ctxt)) {
1588         TM_ERR(core,INIT,"problem creating addr_ctxt\n");
1589         goto out_err;
1590     }
1591
1592     tm->access_type = v3_create_htable(0, tm_hash_buf_fn, tm_eq_buf_fn);
1593     if (!(tm->access_type)) {
1594         TM_ERR(core,INIT,"problem creating access_type\n");
1595         goto out_err1;
1596     }
1597     
1598     v3_lock_init(&(tm->addr_ctxt_lock));
1599     v3_lock_init(&(tm->access_type_lock));
1600
1601     tm->TM_STATE = TM_NULL;
1602     tm->TM_MODE  = TM_OFF;
1603     tm->TM_ABORT = 0;
1604     tm->ginfo    = core;
1605     tm->t_num = 0;
1606     tm->to_branch = 0;
1607     tm->offset = 0;
1608     tm->access_type_entries = 0;
1609     tm->addr_ctxt_entries = 0;
1610     tm->dirty_instr_flag = 0;
1611
1612     /* TODO: Cache Model */
1613     //tm->box = (struct cache_box *)V3_Malloc(sizeof(struct cache_box *));
1614     //tm->box->init = init_cache;
1615     //tm->box->init(sample_spec, tm->box);
1616
1617     *core_data = tm;
1618
1619     return 0;
1620
1621 out_err1:
1622     v3_free_htable(tm->addr_ctxt, 0, 0);
1623 out_err:
1624     V3_Free(tm);
1625     return -1;
1626 }
1627
1628
1629 static int 
1630 deinit_trans_mem (struct v3_vm_info * vm, void * priv_data) 
1631 {
1632     struct v3_tm_state * tms = (struct v3_tm_state *)priv_data;
1633
1634     if (v3_remove_hypercall(vm, TM_KICKBACK_CALL) == -1) {
1635         PrintError(vm, VCORE_NONE, "Problem removing TM hypercall\n");
1636         return -1;
1637     }
1638
1639     v3_lock_deinit(&(tms->lock));
1640
1641     if (tms) {
1642         V3_Free(tms);
1643     }
1644
1645     return 0;
1646 }
1647
1648
1649 static int 
1650 deinit_trans_mem_core (struct guest_info * core, 
1651                        void * priv_data, 
1652                        void * core_data) 
1653 {
1654     struct v3_trans_mem * tm = (struct v3_trans_mem *)core_data;
1655     struct hashtable_iter * ctxt_iter = NULL;
1656
1657     v3_clear_tm_lists(tm);
1658
1659     if (tm->staging_page) {
1660         TM_ERR(core,DEINIT CORE,"WARNING: staging page not freed!\n");
1661     }
1662
1663     ctxt_iter = v3_create_htable_iter(tm->addr_ctxt);
1664     if (!ctxt_iter) {
1665         TM_DBG(core,DEINIT_CORE,"could not create htable iterator\n");
1666         return -1;
1667     }
1668
1669     /* delete all context entries for each hashed address */
1670     while (ctxt_iter->entry) {
1671         struct hash_chain * tmp;
1672         struct hash_chain * curr;
1673         struct list_head * chain_list;
1674         addr_t gva;
1675
1676         gva = (addr_t)v3_htable_get_iter_key(ctxt_iter);
1677         chain_list = (struct list_head *)v3_htable_get_iter_value(ctxt_iter);
1678
1679         /* delete the context */
1680         list_for_each_entry_safe(curr, tmp, chain_list, lt_node) {
1681             tm_del_stale_ctxt(curr);
1682         }
1683
1684         v3_htable_iter_advance(ctxt_iter);
1685     }
1686
1687     v3_destroy_htable_iter(ctxt_iter);
1688
1689     /* we've already deleted the values in this one */
1690     v3_free_htable(tm->addr_ctxt, 0, 0);
1691
1692     /* KCH WARNING: we may not want to free access type values here */
1693     v3_free_htable(tm->access_type, 1, 0);
1694
1695     v3_lock_deinit(&(tm->addr_ctxt_lock));
1696     v3_lock_deinit(&(tm->access_type_lock));
1697
1698     if (tm) {
1699         V3_Free(tm);
1700     }
1701
1702     return 0;
1703 }
1704
1705
1706 static struct v3_extension_impl trans_mem_impl = {
1707     .name = "trans_mem",
1708     .init = NULL,
1709     .vm_init = init_trans_mem,
1710     .vm_deinit = deinit_trans_mem,
1711     .core_init = init_trans_mem_core,
1712     .core_deinit = deinit_trans_mem_core,
1713     .on_entry = NULL,
1714     .on_exit = NULL
1715 };
1716
1717 register_extension(&trans_mem_impl);
1718
1719
1720 /* entry conditions
1721  * tms->on => commit our list, free sp, clear our lists, clr_tm will handle global state, then gc
1722  * tms->off => commit our list, free sp, clear our lists, clr_tm will handle global state, then gc
1723  */
1724 static int 
1725 tm_handle_xend (struct guest_info * core,
1726                 struct v3_trans_mem * tm)
1727 {
1728     rdtscll(tm->exit_time);
1729
1730     // Error checking! make sure that we have gotten here in a legitimate manner
1731     if (tm->TM_MODE != TM_ON) {
1732         TM_ERR(core, UD, "Encountered XEND while not in a transactional region\n");
1733         v3_free_staging_page(tm);
1734         v3_clr_vtlb(core);
1735         v3_clear_tm_lists(tm);
1736         v3_raise_exception(core, UD_EXCEPTION);
1737         return 0;
1738     }
1739
1740     /* Our transaction finished! */
1741     /* Copy over data from the staging page */
1742     TM_DBG(core, UD,"Copying data from our staging page back into 'real' memory\n");
1743
1744     if (commit_list(core, tm) == -1) {
1745         TM_ERR(core,UD,"error commiting tm list to memory\n");
1746         return -1;
1747     }
1748
1749     TM_DBG(core,UD,"Freeing staging page and internal data structures\n");
1750
1751     // Free the staging page
1752     if (v3_free_staging_page(tm) == -1) {
1753         TM_ERR(core,XEND,"couldnt free staging page\n");
1754         return -1;
1755     }
1756
1757     // clear vtlb, as it may still contain our staging page
1758     if (v3_clr_vtlb(core) == -1) {
1759         TM_ERR(core,XEND,"couldnt clear vtlb\n");
1760         return -1;
1761     }
1762
1763     // Clear the lists
1764     v3_clear_tm_lists(tm);
1765
1766     /* Set the state and advance the RIP */
1767     TM_DBG(core,XEND,"advancing rip to %llx\n", core->rip + XEND_INSTR_LEN);
1768     core->rip += XEND_INSTR_LEN; 
1769
1770     v3_clr_tm(tm);
1771
1772     // time to garbage collect
1773     v3_tm_inc_tnum(tm);
1774     if (tm_hash_gc(tm) == -1) {
1775         TM_ERR(core,XEND,"could not gc!\n");
1776         return -1;
1777     }
1778
1779     return 0;
1780 }
1781
1782
1783 /* entry conditions
1784  * tms->on => handle our abort code, handle_trans_abort will clear necessary state
1785  * tms->off => handle our abort code, handle_trans_abort will clear necessary state
1786  */
1787 static int
1788 tm_handle_xabort (struct guest_info * core,
1789                   struct v3_trans_mem * tm)
1790 {
1791         /* TODO: this probably needs to move somewhere else */
1792         rdtscll(tm->exit_time);
1793
1794         // Error checking! make sure that we have gotten here in a legitimate manner
1795         if (tm->TM_MODE != TM_ON) {
1796             TM_DBG(core, UD, "We got here while not in a transactional core!\n");
1797             v3_raise_exception(core, UD_EXCEPTION);
1798         }
1799
1800         TM_DBG(core,UD,"aborting\n");
1801
1802         if (tm->TM_STATE != TM_NULL) {
1803             v3_restore_dirty_instr(core);
1804         }
1805
1806         // Handle the exit
1807         v3_handle_trans_abort(core);
1808
1809         return 0;
1810 }
1811
1812
1813 /* entry conditions
1814  * tms->on => we set up our running env, set_tm will clear other vtlb's to start single stepping
1815  * tms->off => we set up our running env, set_tm will not clear anyone elses vtlb
1816  */
1817 static int
1818 tm_handle_xbegin (struct guest_info * core,
1819                   struct v3_trans_mem * tm,
1820                   uchar_t * instr)
1821 {
1822     sint32_t rel_addr = 0;
1823
1824     if (tm->TM_MODE == TM_ON) {
1825         TM_ERR(core,UD,"We got here while already in a transactional region!");
1826         v3_raise_exception(core, UD_EXCEPTION);
1827     }
1828
1829     rdtscll(tm->entry_time);
1830     tm->entry_exits = core->num_exits;
1831
1832     /* set the tm_mode for this core */
1833     v3_set_tm(tm);
1834
1835     TM_DBG(core,UD,"Set the system in TM Mode, save fallback address");
1836
1837     // Save the fail_call address (first 2 bytes = opcode, last 4 = fail call addr)
1838     rel_addr = *(sint32_t*)(instr+2);
1839     tm->fail_call = core->rip + XBEGIN_INSTR_LEN + rel_addr;
1840
1841     TM_DBG(core,UD,"we set fail_call to %llx, rip is %llx, rel_addr is %x", (uint64_t)tm->fail_call,(uint64_t)core->rip,rel_addr);
1842
1843     /* flush the shadow page tables */
1844     TM_DBG(core,UD,"Throwing out the shadow table");
1845     v3_clr_vtlb(core);
1846
1847     // Increase RIP, ready to go to next instruction
1848     core->rip += XBEGIN_INSTR_LEN; 
1849
1850     return 0;
1851 }
1852
1853
1854 /* entry conditions
1855  * tms->on => we set up our running env, set_tm will clear other vtlb's to start single stepping
1856  * tms->off => we set up our running env, set_tm will not clear anyone elses vtlb
1857  */
1858 static int
1859 tm_handle_xtest (struct guest_info * core,
1860                  struct v3_trans_mem * tm)
1861 {
1862     // if we are in tm mode, set zf to 0, otherwise 1
1863     if (tm->TM_MODE == TM_ON) {
1864         core->ctrl_regs.rflags &= ~(1ULL << 6);
1865     } else {
1866         core->ctrl_regs.rflags |= (1ULL << 6);
1867     }
1868
1869     core->rip += XTEST_INSTR_LEN;
1870
1871     return 0;
1872 }
1873
1874
1875 /* instructions:
1876  * XBEGIN c7 f8 rel32
1877  * XABORT c6 f8 imm8
1878  * XEND   0f 01 d5
1879  */
1880 static int 
1881 tm_handle_ud (struct guest_info * core) 
1882 {
1883     struct v3_trans_mem * tm = (struct v3_trans_mem *)v3_get_ext_core_state(core, "trans_mem");
1884     uchar_t instr[INSTR_BUF_SZ];
1885     uint8_t byte1, byte2, byte3;
1886
1887     tm_read_instr(core, (addr_t)core->rip, instr, INSTR_BUF_SZ);
1888
1889     byte1 = *(uint8_t *)((addr_t)instr);
1890     byte2 = *(uint8_t *)((addr_t)instr + 1);
1891     byte3 = *(uint8_t *)((addr_t)instr + 2);
1892
1893
1894     if (byte1 == 0xc7 && byte2 == 0xf8) {  /* third byte is an immediate */
1895
1896         TM_DBG(core,UD,"Encountered Haswell-specific XBEGIN %x %x %d at %llx", byte1, byte2, byte3, (uint64_t)core->rip);
1897
1898         if (tm_handle_xbegin(core, tm, instr) == -1) {
1899             TM_ERR(core, UD, "Problem handling XBEGIN\n");
1900             return -1;
1901         }
1902
1903     } else if (byte1 == 0xc6 && byte2 == 0xf8) { /* third byte is an immediate */
1904
1905         TM_DBG(core, UD, "Encountered Haswell-specific XABORT %x %x %d at %llx\n", byte1, byte2, byte3, (uint64_t)core->rip);
1906
1907         if (tm_handle_xabort(core, tm) == -1) {
1908             TM_ERR(core, UD, "Problem handling XABORT\n");
1909             return -1;
1910         }
1911
1912     } else if (byte1 == 0x0f && byte2 == 0x01 && byte3 == 0xd5) {
1913
1914         TM_DBG(core, UD, "Encountered Haswell-specific XEND %x %x %d at %llx\n", byte1, byte2, byte3, (uint64_t)core->rip);
1915
1916         if (tm_handle_xend(core, tm) == -1) {
1917             TM_ERR(core, UD, "Problem handling XEND\n");
1918             return -1;
1919         }
1920
1921
1922     } else if (byte1 == 0x0f && byte2 == 0x01 && byte3 == 0xd6) {  /* third byte is an immediate */
1923
1924         TM_DBG(core,UD,"Encountered Haswell-specific XTEST %x %x %x at %llx\n", byte1, byte2, byte3, (uint64_t)core->rip);
1925
1926         if (tm_handle_xtest(core, tm) == -1) {
1927             TM_ERR(core, UD, "Problem handling XTEST\n");
1928             return -1;
1929         }
1930
1931     } else {
1932
1933         /* oh no, this is still unknown, pass the error back to the guest! */
1934         TM_DBG(core,UD,"Encountered:%x %x %x\n", byte1, byte2, byte3);
1935         v3_raise_exception(core, UD_EXCEPTION);
1936     }
1937
1938     return 0;
1939 }
1940
1941
1942 int 
1943 v3_tm_handle_exception (struct guest_info * info,
1944                         addr_t exit_code)
1945 {
1946     struct v3_trans_mem * tm = (struct v3_trans_mem *)v3_get_ext_core_state(info, "trans_mem");
1947
1948     if (!tm) {
1949         TM_ERR(info,ERR,"TM extension state not found\n");
1950         return -1;
1951     } 
1952
1953     switch (exit_code) {
1954         /* any of these exceptions should abort current transactions */
1955         case SVM_EXIT_EXCP6:
1956             if (tm_handle_ud(info) == -1) {
1957                 return -1;
1958             }
1959             break;
1960         case SVM_EXIT_EXCP0:
1961             if (tm->TM_MODE != TM_ON) {
1962                 v3_raise_exception(info, DE_EXCEPTION);
1963             }
1964             else {
1965                 TM_DBG(info,EXCP,"aborting due to DE exception\n");
1966                 v3_handle_trans_abort(info);
1967             }
1968             break;
1969         case SVM_EXIT_EXCP1:
1970             if (tm->TM_MODE != TM_ON) {
1971                 v3_raise_exception(info, DB_EXCEPTION);
1972             }
1973             else {
1974                 TM_DBG(info,EXCP,"aborting due to DB exception\n");
1975                 v3_handle_trans_abort(info);
1976             }
1977             break;
1978         case SVM_EXIT_EXCP3:
1979             if (tm->TM_MODE != TM_ON) {
1980                 v3_raise_exception(info, BP_EXCEPTION);
1981             }
1982             else {
1983                 TM_DBG(info,EXCP,"aborting due to BP exception\n");
1984                 v3_handle_trans_abort(info);
1985             }
1986             break;
1987         case SVM_EXIT_EXCP4:
1988             if (tm->TM_MODE != TM_ON) {
1989                 v3_raise_exception(info, OF_EXCEPTION);
1990             }
1991             else {
1992                 TM_DBG(info,EXCP,"aborting due to OF exception\n");
1993                 v3_handle_trans_abort(info);
1994             }
1995             break;
1996         case SVM_EXIT_EXCP5:
1997             if (tm->TM_MODE != TM_ON) {
1998                 v3_raise_exception(info, BR_EXCEPTION);
1999             }
2000             else {
2001                 TM_DBG(info,EXCP,"aborting due to BR exception\n");
2002                 v3_handle_trans_abort(info);
2003             }
2004             break;
2005         case SVM_EXIT_EXCP7:
2006             if (tm->TM_MODE != TM_ON) {
2007                 v3_raise_exception(info, NM_EXCEPTION);
2008             }
2009             else {
2010                 TM_DBG(info,EXCP,"aborting due to NM exception\n");
2011                 v3_handle_trans_abort(info);
2012             }
2013             break;
2014         case SVM_EXIT_EXCP10:
2015             if (tm->TM_MODE != TM_ON) {
2016                 v3_raise_exception(info, TS_EXCEPTION);
2017             }
2018             else {
2019                 TM_DBG(info,EXCP,"aborting due to TS exception\n");
2020                 v3_handle_trans_abort(info);
2021             }
2022             break;
2023         case SVM_EXIT_EXCP11:
2024             if (tm->TM_MODE != TM_ON) {
2025                 v3_raise_exception(info, NP_EXCEPTION);
2026             }
2027             else {
2028                 TM_DBG(info,EXCP,"aborting due to NP exception\n");
2029                 v3_handle_trans_abort(info);
2030             }
2031             break;
2032         case SVM_EXIT_EXCP12:
2033             if (tm->TM_MODE != TM_ON) {
2034                 v3_raise_exception(info, SS_EXCEPTION);
2035             }
2036             else {
2037                 TM_DBG(info,EXCP,"aborting due to SS exception\n");
2038                 v3_handle_trans_abort(info);
2039             }
2040             break;
2041         case SVM_EXIT_EXCP13:
2042             if (tm->TM_MODE != TM_ON) {
2043                 v3_raise_exception(info, GPF_EXCEPTION);
2044             }
2045             else {
2046                 TM_DBG(info,EXCP,"aborting due to GPF exception\n");
2047                 v3_handle_trans_abort(info);
2048             }
2049             break;
2050         case SVM_EXIT_EXCP16:
2051             if (tm->TM_MODE != TM_ON) {
2052                 v3_raise_exception(info, MF_EXCEPTION);
2053             }
2054             else {
2055                 TM_DBG(info,EXCP,"aborting due to MF exception\n");
2056                 v3_handle_trans_abort(info);
2057             }
2058             break;
2059         case SVM_EXIT_EXCP17:
2060             if (tm->TM_MODE != TM_ON) {
2061                 v3_raise_exception(info, AC_EXCEPTION);
2062             }
2063             else {
2064                 TM_DBG(info,EXCP,"aborting due to AC exception\n");
2065                 v3_handle_trans_abort(info);
2066             }
2067             break;
2068         case SVM_EXIT_EXCP19:
2069             if (tm->TM_MODE != TM_ON) {
2070                 v3_raise_exception(info, XF_EXCEPTION);
2071             }
2072             else {
2073                 TM_DBG(info,EXCP,"aborting due to XF exception\n");
2074                 v3_handle_trans_abort(info);
2075             }
2076             break;
2077
2078             TM_DBG(info,EXCP,"exception # %d\n", (int)exit_code - 0x40);
2079     }
2080
2081     return 0;
2082 }
2083
2084
2085 void 
2086 v3_tm_set_excp_intercepts (vmcb_ctrl_t * ctrl_area) 
2087 {
2088     ctrl_area->exceptions.de = 1; // 0  : divide by zero
2089     ctrl_area->exceptions.db = 1; // 1  : debug
2090     ctrl_area->exceptions.bp = 1; // 3  : breakpoint
2091     ctrl_area->exceptions.of = 1; // 4  : overflow
2092     ctrl_area->exceptions.br = 1; // 5  : bound range
2093     ctrl_area->exceptions.ud = 1; // 6  : undefined opcode
2094     ctrl_area->exceptions.nm = 1; // 7  : device not available
2095     ctrl_area->exceptions.ts = 1; // 10 : invalid tss
2096     ctrl_area->exceptions.np = 1; // 11 : segment not present
2097     ctrl_area->exceptions.ss = 1; // 12 : stack
2098     ctrl_area->exceptions.gp = 1; // 13 : general protection
2099     ctrl_area->exceptions.mf = 1; // 16 : x87 exception pending
2100     ctrl_area->exceptions.ac = 1; // 17 : alignment check
2101     ctrl_area->exceptions.xf = 1; // 19 : simd floating point
2102 }
2103
2104
2105 extern void v3_stgi();
2106 extern void v3_clgi();
2107
2108 /* 441-tm: if we are in TM mode, we need to check for any interrupts here,
2109  * and if there are any, need to do some aborting! Make sure not to die here
2110  * if we are already 'aborting', this results in infiloop
2111  */
2112 void 
2113 v3_tm_check_intr_state (struct guest_info * info, 
2114                         vmcb_ctrl_t * guest_ctrl,
2115                         vmcb_saved_state_t * guest_state)
2116                         
2117 {
2118     struct v3_trans_mem * tm = (struct v3_trans_mem *)v3_get_ext_core_state(info, "trans_mem");
2119
2120     if (!tm) {
2121         TM_ERR(info,INTR,"TM extension state not found\n");
2122         v3_stgi();
2123         return;
2124     }
2125
2126     /* TODO: work this in */
2127     if (0 && (tm->TM_MODE == TM_ON) && 
2128              (tm->TM_ABORT != 1)) {
2129
2130         if (guest_ctrl->guest_ctrl.V_IRQ ||
2131             guest_ctrl->EVENTINJ.valid) {
2132
2133             rdtscll(tm->exit_time);
2134             TM_DBG(info,INTR,"%lld exits happened, time delta is %lld",(info->num_exits - tm->entry_exits),(tm->entry_time - tm->exit_time));
2135
2136             // We do indeed have pending interrupts
2137             v3_stgi();
2138             TM_DBG(info,INTR,"we have a pending interrupt!\n");
2139
2140             v3_handle_trans_abort(info);
2141             // Copy new RIP state into arch dependent structure
2142             guest_state->rip = info->rip;
2143             TM_DBG(info,INTR,"currently guest state rip is %llx\n",(uint64_t)guest_state->rip);
2144             v3_clgi();
2145         }
2146
2147     }
2148
2149 }
2150
2151
2152 int
2153 v3_tm_handle_pf_64 (struct guest_info * info,
2154                     pf_error_t error_code,
2155                     addr_t fault_addr,
2156                     addr_t * page_to_use)
2157 {
2158     struct v3_trans_mem * tm = (struct v3_trans_mem *)v3_get_ext_core_state(info, "trans_mem");
2159     struct v3_tm_state * tms = (struct v3_tm_state *)v3_get_extension_state(info->vm_info, "trans_mem");
2160
2161     if (!tm) {
2162         TM_ERR(info,HANDLE_PF, "couldn't get tm core state\n");
2163         return -1;
2164     }
2165
2166     if (!tms) {
2167         TM_ERR(info,HANDLE_PF, "couldn't get tm global state\n");
2168         return -1;
2169     }
2170
2171     if ((tms->TM_MODE == TM_ON) && 
2172             (error_code.user == 1)) {
2173
2174         TM_DBG(info,PF,"Core reporting in, got a #PF (tms->mode is %d)\n", tms->TM_MODE);
2175
2176         *page_to_use = v3_handle_trans_mem_fault(info, fault_addr,  error_code);
2177
2178         if (*page_to_use == ERR_TRANS_FAULT_FAIL){
2179             TM_ERR(info,HANDLE_PF, "could not handle transaction page fault\n");
2180             return -1;
2181         }
2182
2183         if ((tm->TM_MODE == TM_ON) && 
2184                 (tm->staging_page == NULL)) {
2185
2186             tm->staging_page = V3_AllocPages(1);
2187
2188             if (!(tm->staging_page)) {
2189                 TM_ERR(info,MMU,"Problem allocating staging page\n");
2190                 return -1;
2191             }
2192
2193             TM_DBG(info,MMU,"Created staging page at %p\n", (void *)tm->staging_page);
2194         }
2195     }
2196
2197     return 0;
2198 }
2199
2200
2201 void 
2202 v3_tm_handle_usr_tlb_miss (struct guest_info * info,
2203                            pf_error_t error_code,
2204                            addr_t page_to_use,
2205                            addr_t * shadow_pa)
2206 {
2207     struct v3_trans_mem * tm = (struct v3_trans_mem *)v3_get_ext_core_state(info, "trans_mem");
2208
2209     /* TLB miss from user */
2210     if ((tm->TM_MODE == TM_ON) && 
2211             (error_code.user == 1)) {
2212
2213         if (page_to_use > TRANS_FAULT_OK) {
2214             TM_DBG(info,MMU, "Using alternate page at: %llx\n", (uint64_t)page_to_use);
2215             *shadow_pa = page_to_use;
2216         }
2217
2218     }
2219
2220 }
2221
2222
2223 void
2224 v3_tm_handle_read_fault (struct guest_info * info,
2225                          pf_error_t error_code,
2226                          pte64_t * shadow_pte)
2227 {
2228     struct v3_trans_mem * tm = (struct v3_trans_mem *)v3_get_ext_core_state(info, "trans_mem");
2229     struct v3_tm_state * tms = (struct v3_tm_state *)v3_get_extension_state(info->vm_info, "trans_mem");
2230
2231     // If we are about to read, make it read only 
2232     if ((tms->TM_MODE == TM_ON) && 
2233         (tm->TM_STATE == TM_EXEC) && 
2234         (error_code.write == 0) && 
2235         (error_code.user == 1)) {
2236
2237         TM_DBG(info,MMU, "Flagging the page read only\n");
2238         shadow_pte->writable = 0;
2239     }
2240 }
2241
2242
2243 int 
2244 v3_tm_decode_rtm_instrs (struct guest_info * info,
2245                          addr_t instr_ptr,
2246                          struct x86_instr * instr)
2247 {
2248     uint8_t byte1, byte2, byte3;
2249     struct v3_trans_mem * tm = (struct v3_trans_mem *)v3_get_ext_core_state(info, "trans_mem");
2250
2251     if (tm->TM_MODE == TM_ON) {
2252
2253         byte1 = *(uint8_t *)(instr_ptr);
2254         byte2 = *(uint8_t *)(instr_ptr + 1);
2255         byte3 = *(uint8_t *)(instr_ptr + 2);
2256
2257         if (byte1 == 0xc7 && 
2258             byte2 == 0xf8) {  /* third byte is an immediate */
2259
2260             TM_DBG(info, DECODE,"Decoding XBEGIN %x %x %d\n", byte1, byte2, byte3);
2261             instr->instr_length = 6;
2262             return 0;
2263
2264         } else if (byte1 == 0xc6 && 
2265                    byte2 == 0xf8) { /* third byte is an immediate */
2266
2267             TM_DBG(info, DECODE, "Decoding XABORT %x %x %d\n", byte1, byte2, byte3);
2268             instr->instr_length = 3;
2269             return 0;
2270
2271         } else if (byte1 == 0x0f && 
2272                    byte2 == 0x01 && 
2273                    byte3 == 0xd5) {
2274
2275             TM_DBG(info, DECODE, "Decoding XEND %x %x %x\n", byte1, byte2, byte3);
2276             instr->instr_length = 3;
2277             return 0;
2278
2279         }
2280
2281     }
2282
2283     return 0;
2284 }
2285