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.


Cleanup based on cppcheck pass (Devices and Extensions)
[palacios.git] / palacios / src / extensions / ext_trans_mem.c
index 82c89ee..7fb8164 100644 (file)
 #define PrintDebug(fmt, args...)
 #endif
 
-/* TODO LIST: 
- * - save/restore register state on XBEGIN/XABORT
- * - put status codes in RAX
- * - Implement proper exceptions for failed XBEGINS etc.
- */
 
 /* this includes a mov to rax */
 static const char * vmmcall_bytes = "\x48\xc7\xc0\x37\x13\x00\x00\x0f\x01\xd9"; 
@@ -241,7 +236,7 @@ v3_store_next_instr (struct guest_info * core, struct v3_trans_mem * tm)
         /* this will attempt to abort all the remote cores */
         if (tm_handle_decode_fail(core) == -1) {
             TM_ERR(core,Error,"Could not handle failed decode\n");
-            return -1;
+            return ERR_STORE_FAIL;
         }
 
         /* we need to trigger a local abort */
@@ -378,7 +373,7 @@ tm_handle_fault_ifetch (struct guest_info * core,
         return ERR_TRANS_FAULT_FAIL;
     } else if (sto == ERR_STORE_MUST_ABORT) {
         TM_DBG(core,EXIT,"aborting for some reason\n");
-        v3_handle_trans_abort(core);
+        v3_handle_trans_abort(core, TM_ABORT_UNSPECIFIED, 0);
         return TRANS_FAULT_OK;
     }
 
@@ -512,7 +507,7 @@ tm_handle_fault_extern_ifetch (struct guest_info * core,
 
     } else if (sto == ERR_STORE_MUST_ABORT) {
         TM_ERR(core,IFETCH,"decode failed, going out of single stepping\n");
-        v3_handle_trans_abort(core);
+        v3_handle_trans_abort(core, TM_ABORT_UNSPECIFIED, 0);
         return TRANS_FAULT_OK;
     }
 
@@ -663,7 +658,7 @@ tm_handle_hcall_dec_abort (struct guest_info * core,
     TM_DBG(core,EXIT,"we are in ABORT, call the abort handler\n");
     tm->TM_ABORT = 0;
 
-    v3_handle_trans_abort(core);
+    v3_handle_trans_abort(core, TM_ABORT_UNSPECIFIED, 0);
 
     TM_DBG(core,EXIT,"RIP after abort: %p\n", ((void*)(core->rip)));
 
@@ -708,7 +703,7 @@ tm_check_list_conflict (struct guest_info * core,
         } else if (conflict == CHECK_IS_CONFLICT) {
 
             TM_DBG(core,EXIT,"we have a conflict, aborting\n");
-            v3_handle_trans_abort(core);
+            v3_handle_trans_abort(core, TM_ABORT_CONFLICT, 0);
             return CHECK_MUST_ABORT;
 
         }
@@ -837,8 +832,43 @@ v3_tm_inc_tnum (struct v3_trans_mem * tm)
 }
 
 
+static void
+tm_set_abort_status (struct guest_info * core, 
+                     tm_abrt_cause_t cause, 
+                     uint8_t xabort_reason)
+{
+    core->vm_regs.rax = 0;
+
+    switch (cause) {
+        case TM_ABORT_XABORT:
+            // we put the xabort immediate in eax 31:24
+            // cause is zero
+            core->vm_regs.rax |= (xabort_reason << 24);
+            break;
+        case TM_ABORT_CONFLICT:
+            // if this was a conflict from another core, it may work
+            // if we try again
+            core->vm_regs.rax |= (1 << ABORT_CONFLICT) | (1 << ABORT_RETRY);
+            break;
+        case TM_ABORT_INTERNAL:
+        case TM_ABORT_BKPT:
+            core->vm_regs.rax |= (1 << cause);
+            break;
+        case TM_ABORT_UNSPECIFIED:
+            // just return 0 in EAX
+            break;
+        default:
+            TM_ERR(core, ABORT, "invalid abort cause\n");
+            break;
+    }
+}
+
+
+// xabort_reason is only used for XABORT instruction
 int 
-v3_handle_trans_abort (struct guest_info * core) 
+v3_handle_trans_abort (struct guest_info * core, 
+                       tm_abrt_cause_t cause, 
+                       uint8_t xabort_reason)
 {
     struct v3_trans_mem * tm = (struct v3_trans_mem *)v3_get_ext_core_state(core, "trans_mem");
 
@@ -870,7 +900,8 @@ v3_handle_trans_abort (struct guest_info * core)
         v3_tm_inc_tnum(tm);
     }
     
-  
+    tm_set_abort_status(core, cause, xabort_reason);
+
     // time to garbage collect
     if (tm_hash_gc(tm) == -1) {
         TM_ERR(core,GC,"could not gc!\n");
@@ -1108,28 +1139,20 @@ static int
 tm_collect_context (struct v3_trans_mem * tm, 
                     struct hashtable_iter * ctxt_iter, 
                     struct hash_chain * curr, 
-                    uint64_t * begin_time,
-                    uint64_t * end_time,
                     addr_t gva)
 {
         uint64_t i;
 
         for (i = 0; i < tm->ginfo->vm_info->num_cores; i++) {
-            void * buf[3];
+            struct v3_ctxt_tuple tup;
             struct v3_tm_access_type * type;
             addr_t key;
 
-            rdtscll(*end_time);
-            if ((*end_time - *begin_time) > 100000000) {
-                TM_ERR(tm->ginfo,GC,"time threshhold exceeded, exiting!!!\n");
-                return -1;
-            }
-            
-            buf[0] = (void *)gva;
-            buf[1] = (void *)i;
-            buf[2] = (void *)curr->curr_lt[i];
+            tup.gva     = (void *)gva;
+            tup.core_id = (void *)i;
+            tup.core_lt = (void *)curr->curr_lt[i];
 
-            key = v3_hash_buffer((uchar_t*)buf, sizeof(void*)*3);
+            key = v3_hash_buffer((uchar_t*)&tup, sizeof(struct v3_ctxt_tuple));
 
             type = (struct v3_tm_access_type *)v3_htable_search(tm->access_type, key);
 
@@ -1153,9 +1176,7 @@ static int
 tm_collect_all_contexts (struct v3_trans_mem * tm,
                          struct hashtable_iter * ctxt_iter,
                          uint64_t tmoff,
-                         uint64_t * lt_copy,
-                         uint64_t * begin_time,
-                         uint64_t * end_time)
+                         uint64_t * lt_copy)
 {
     struct hash_chain * tmp;
     struct hash_chain * curr;
@@ -1183,7 +1204,7 @@ tm_collect_all_contexts (struct v3_trans_mem * tm,
         TM_DBG(tm->ginfo,GC,"garbage collecting entries for address %llx\n", (uint64_t)gva);
 
         /* found one, delete corresponding entries in access_type */
-        if (tm_collect_context(tm, ctxt_iter, curr, begin_time, end_time, gva) == -1) {
+        if (tm_collect_context(tm, ctxt_iter, curr, gva) == -1) {
             TM_ERR(tm->ginfo,GC,"ERROR collecting context\n");
             return -1;
         }
@@ -1210,7 +1231,7 @@ tm_hash_gc (struct v3_trans_mem * tm)
 {
     addr_t irqstate, irqstate2;
     int rc = 0;
-    uint64_t begin_time, end_time, tmoff;
+    uint64_t tmoff;
     uint64_t * lt_copy;
     struct v3_tm_state * tms = NULL;
     struct hashtable_iter * ctxt_iter = NULL;
@@ -1235,8 +1256,6 @@ tm_hash_gc (struct v3_trans_mem * tm)
 
     memset(lt_copy, 0, sizeof(uint64_t)*(tm->ginfo->vm_info->num_cores));
 
-    rdtscll(begin_time);
-
     /* lt_copy holds the last transaction number for each core */
     irqstate = v3_lock_irqsave(tm_global_state->lock);
     memcpy(lt_copy, tm_global_state->last_trans, sizeof(uint64_t)*(tm->ginfo->vm_info->num_cores));
@@ -1257,7 +1276,7 @@ tm_hash_gc (struct v3_trans_mem * tm)
     /* we check each address stored in the hash */
     while (ctxt_iter->entry) {
         /* NOTE: this call advances the hash iterator */
-        if (tm_collect_all_contexts(tm, ctxt_iter, tmoff, lt_copy, &begin_time, &end_time) == -1) {
+        if (tm_collect_all_contexts(tm, ctxt_iter, tmoff, lt_copy) == -1) {
             rc = -1;
             goto out1;
         }
@@ -1270,12 +1289,10 @@ out:
     v3_unlock_irqrestore(tm->access_type_lock, irqstate);
     v3_unlock_irqrestore(tm->addr_ctxt_lock, irqstate2);
 
-    rdtscll(end_time);
-
     if (rc == -1) {
-        TM_ERR(tm->ginfo,GC,"garbage collection failed, time spent: %d cycles\n", (int)(end_time - begin_time));
+        TM_ERR(tm->ginfo,GC,"garbage collection failed\n");
     } else {
-        TM_DBG(tm->ginfo,GC,"ended garbage collection succesfuly, time spent: %d cycles\n", (int)(end_time - begin_time));
+        TM_DBG(tm->ginfo,GC,"ended garbage collection succesfuly\n");
     }
 
     TM_DBG(tm->ginfo,GC,"\t %d entries in addr_ctxt (post)\n", (int)v3_htable_count(tm->addr_ctxt));
@@ -1508,6 +1525,34 @@ tm_record_access (struct  v3_trans_mem * tm,
 }
 
 
+static void
+tm_prepare_cpuid (struct v3_vm_info * vm)
+{
+
+    V3_Print(vm, VCORE_NONE, "TM INIT | enabling RTM cap in CPUID\n");
+
+    /* increase max CPUID function to 7 (extended feature flags enumeration) */
+    v3_cpuid_add_fields(vm,0x0,    
+            0xf, 0x7,      
+            0, 0,
+            0, 0,
+            0, 0);
+
+
+    /* do the same for AMD */
+    v3_cpuid_add_fields(vm,0x80000000, 
+            0xffffffff, 0x80000007,   
+            0, 0,
+            0, 0,
+            0, 0);
+
+
+    /* enable RTM (CPUID.07H.EBX.RTM = 1) */
+    v3_cpuid_add_fields(vm, 0x07, 0, 0, (1<<11), 0, 0, 0, 0, 0);
+    v3_cpuid_add_fields(vm, 0x80000007, 0, 0, (1<<11), 0, 0, 0, 0, 0);
+}
+
+
 static int 
 init_trans_mem (struct v3_vm_info * vm, 
                 v3_cfg_tree_t * cfg, 
@@ -1553,6 +1598,8 @@ init_trans_mem (struct v3_vm_info * vm,
     *priv_data = tms;
     tm_global_state = tms;
 
+    tm_prepare_cpuid(vm);
+
     return 0;
 
 out_err1:
@@ -1695,9 +1742,7 @@ deinit_trans_mem_core (struct guest_info * core,
     v3_lock_deinit(&(tm->addr_ctxt_lock));
     v3_lock_deinit(&(tm->access_type_lock));
 
-    if (tm) {
-        V3_Free(tm);
-    }
+    V3_Free(tm);
 
     return 0;
 }
@@ -1725,16 +1770,14 @@ static int
 tm_handle_xend (struct guest_info * core,
                 struct v3_trans_mem * tm)
 {
-    rdtscll(tm->exit_time);
 
-    // Error checking! make sure that we have gotten here in a legitimate manner
+    /* XEND should raise a GPF when RTM mode is not on */
     if (tm->TM_MODE != TM_ON) {
         TM_ERR(core, UD, "Encountered XEND while not in a transactional region\n");
-        v3_free_staging_page(tm);
-        v3_clr_vtlb(core);
-        v3_clear_tm_lists(tm);
-        v3_raise_exception(core, UD_EXCEPTION);
+
+        v3_raise_exception(core, GPF_EXCEPTION);
         return 0;
+
     }
 
     /* Our transaction finished! */
@@ -1786,10 +1829,13 @@ tm_handle_xend (struct guest_info * core,
  */
 static int
 tm_handle_xabort (struct guest_info * core,
-                  struct v3_trans_mem * tm)
+                  struct v3_trans_mem * tm,
+                  uchar_t * instr)
 {
-        /* TODO: this probably needs to move somewhere else */
-        rdtscll(tm->exit_time);
+        uint8_t reason; 
+
+        // we must reflect the immediate back into EAX 31:24
+        reason = *(uint8_t*)(instr+2);
 
         // Error checking! make sure that we have gotten here in a legitimate manner
         if (tm->TM_MODE != TM_ON) {
@@ -1804,7 +1850,7 @@ tm_handle_xabort (struct guest_info * core,
         }
 
         // Handle the exit
-        v3_handle_trans_abort(core);
+        v3_handle_trans_abort(core, TM_ABORT_XABORT, reason);
 
         return 0;
 }
@@ -1820,22 +1866,38 @@ tm_handle_xbegin (struct guest_info * core,
                   uchar_t * instr)
 {
     sint32_t rel_addr = 0;
+    uint8_t out_of_bounds = 0;
+    uint8_t in_compat_no_long = 0;
 
     if (tm->TM_MODE == TM_ON) {
-        TM_ERR(core,UD,"We got here while already in a transactional region!");
+        /* TODO: this is actually an indication of nesting, we'll fix this later */
+        TM_ERR(core,UD,"We don't support nested transactions yet!\n");
         v3_raise_exception(core, UD_EXCEPTION);
+        return -1;
     }
 
-    rdtscll(tm->entry_time);
-    tm->entry_exits = core->num_exits;
+    // Save the fail_call address (first 2 bytes = opcode, last 4 = fail call addr)
+    rel_addr = *(sint32_t*)(instr+2);
+
+    /* raise a GPF if we're trying to set a fail call outside of code segment */
+    in_compat_no_long = (core->cpu_mode == LONG_32_COMPAT) || ((struct efer_64*)&(core->ctrl_regs.efer))->lma == 0;
+    out_of_bounds     = (core->rip + rel_addr > core->segments.cs.base + core->segments.cs.limit || 
+                         core->rip + rel_addr < core->segments.cs.base);
+
+    if (in_compat_no_long && out_of_bounds) {
+        v3_raise_exception(core, GPF_EXCEPTION);
+        return 0;
+    }
+
+    /* TODO: also raise GPF if we're in long mode and failcall isn't canonical */
+
 
     /* set the tm_mode for this core */
     v3_set_tm(tm);
 
     TM_DBG(core,UD,"Set the system in TM Mode, save fallback address");
 
-    // Save the fail_call address (first 2 bytes = opcode, last 4 = fail call addr)
-    rel_addr = *(sint32_t*)(instr+2);
+
     tm->fail_call = core->rip + XBEGIN_INSTR_LEN + rel_addr;
 
     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);
@@ -1859,13 +1921,21 @@ static int
 tm_handle_xtest (struct guest_info * core,
                  struct v3_trans_mem * tm)
 {
+    struct rflags * rf = (struct rflags*)&(core->ctrl_regs.rflags);
+
     // if we are in tm mode, set zf to 0, otherwise 1
     if (tm->TM_MODE == TM_ON) {
-        core->ctrl_regs.rflags &= ~(1ULL << 6);
+        rf->zf = 0;
     } else {
-        core->ctrl_regs.rflags |= (1ULL << 6);
+        rf->zf = 1;
     }
 
+    rf->cf = 0;
+    rf->of = 0;
+    rf->sf = 0;
+    rf->pf = 0;
+    rf->af = 0;
+
     core->rip += XTEST_INSTR_LEN;
 
     return 0;
@@ -1904,7 +1974,7 @@ tm_handle_ud (struct guest_info * core)
 
         TM_DBG(core, UD, "Encountered Haswell-specific XABORT %x %x %d at %llx\n", byte1, byte2, byte3, (uint64_t)core->rip);
 
-        if (tm_handle_xabort(core, tm) == -1) {
+        if (tm_handle_xabort(core, tm, instr) == -1) {
             TM_ERR(core, UD, "Problem handling XABORT\n");
             return -1;
         }
@@ -1963,7 +2033,7 @@ v3_tm_handle_exception (struct guest_info * info,
             }
             else {
                 TM_DBG(info,EXCP,"aborting due to DE exception\n");
-                v3_handle_trans_abort(info);
+                v3_handle_trans_abort(info, TM_ABORT_UNSPECIFIED, 0);
             }
             break;
         case SVM_EXIT_EXCP1:
@@ -1972,7 +2042,7 @@ v3_tm_handle_exception (struct guest_info * info,
             }
             else {
                 TM_DBG(info,EXCP,"aborting due to DB exception\n");
-                v3_handle_trans_abort(info);
+                v3_handle_trans_abort(info, TM_ABORT_UNSPECIFIED, 0);
             }
             break;
         case SVM_EXIT_EXCP3:
@@ -1981,7 +2051,7 @@ v3_tm_handle_exception (struct guest_info * info,
             }
             else {
                 TM_DBG(info,EXCP,"aborting due to BP exception\n");
-                v3_handle_trans_abort(info);
+                v3_handle_trans_abort(info, TM_ABORT_UNSPECIFIED, 0);
             }
             break;
         case SVM_EXIT_EXCP4:
@@ -1990,7 +2060,7 @@ v3_tm_handle_exception (struct guest_info * info,
             }
             else {
                 TM_DBG(info,EXCP,"aborting due to OF exception\n");
-                v3_handle_trans_abort(info);
+                v3_handle_trans_abort(info, TM_ABORT_UNSPECIFIED, 0);
             }
             break;
         case SVM_EXIT_EXCP5:
@@ -1999,7 +2069,7 @@ v3_tm_handle_exception (struct guest_info * info,
             }
             else {
                 TM_DBG(info,EXCP,"aborting due to BR exception\n");
-                v3_handle_trans_abort(info);
+                v3_handle_trans_abort(info, TM_ABORT_UNSPECIFIED, 0);
             }
             break;
         case SVM_EXIT_EXCP7:
@@ -2008,7 +2078,7 @@ v3_tm_handle_exception (struct guest_info * info,
             }
             else {
                 TM_DBG(info,EXCP,"aborting due to NM exception\n");
-                v3_handle_trans_abort(info);
+                v3_handle_trans_abort(info, TM_ABORT_UNSPECIFIED, 0);
             }
             break;
         case SVM_EXIT_EXCP10:
@@ -2017,7 +2087,7 @@ v3_tm_handle_exception (struct guest_info * info,
             }
             else {
                 TM_DBG(info,EXCP,"aborting due to TS exception\n");
-                v3_handle_trans_abort(info);
+                v3_handle_trans_abort(info, TM_ABORT_UNSPECIFIED, 0);
             }
             break;
         case SVM_EXIT_EXCP11:
@@ -2026,7 +2096,7 @@ v3_tm_handle_exception (struct guest_info * info,
             }
             else {
                 TM_DBG(info,EXCP,"aborting due to NP exception\n");
-                v3_handle_trans_abort(info);
+                v3_handle_trans_abort(info, TM_ABORT_UNSPECIFIED, 0);
             }
             break;
         case SVM_EXIT_EXCP12:
@@ -2035,7 +2105,7 @@ v3_tm_handle_exception (struct guest_info * info,
             }
             else {
                 TM_DBG(info,EXCP,"aborting due to SS exception\n");
-                v3_handle_trans_abort(info);
+                v3_handle_trans_abort(info, TM_ABORT_UNSPECIFIED, 0);
             }
             break;
         case SVM_EXIT_EXCP13:
@@ -2044,7 +2114,7 @@ v3_tm_handle_exception (struct guest_info * info,
             }
             else {
                 TM_DBG(info,EXCP,"aborting due to GPF exception\n");
-                v3_handle_trans_abort(info);
+                v3_handle_trans_abort(info, TM_ABORT_UNSPECIFIED, 0);
             }
             break;
         case SVM_EXIT_EXCP16:
@@ -2053,7 +2123,7 @@ v3_tm_handle_exception (struct guest_info * info,
             }
             else {
                 TM_DBG(info,EXCP,"aborting due to MF exception\n");
-                v3_handle_trans_abort(info);
+                v3_handle_trans_abort(info, TM_ABORT_UNSPECIFIED, 0);
             }
             break;
         case SVM_EXIT_EXCP17:
@@ -2062,7 +2132,7 @@ v3_tm_handle_exception (struct guest_info * info,
             }
             else {
                 TM_DBG(info,EXCP,"aborting due to AC exception\n");
-                v3_handle_trans_abort(info);
+                v3_handle_trans_abort(info, TM_ABORT_UNSPECIFIED, 0);
             }
             break;
         case SVM_EXIT_EXCP19:
@@ -2071,7 +2141,7 @@ v3_tm_handle_exception (struct guest_info * info,
             }
             else {
                 TM_DBG(info,EXCP,"aborting due to XF exception\n");
-                v3_handle_trans_abort(info);
+                v3_handle_trans_abort(info, TM_ABORT_UNSPECIFIED, 0);
             }
             break;
 
@@ -2119,28 +2189,26 @@ v3_tm_check_intr_state (struct guest_info * info,
 
     if (!tm) {
         TM_ERR(info,INTR,"TM extension state not found\n");
-        v3_stgi();
         return;
     }
 
-    /* TODO: work this in */
-    if (0 && (tm->TM_MODE == TM_ON) && 
-             (tm->TM_ABORT != 1)) {
+    if ((tm->TM_MODE == TM_ON) && 
+        (tm->TM_ABORT != 1)) {
 
         if (guest_ctrl->guest_ctrl.V_IRQ ||
             guest_ctrl->EVENTINJ.valid) {
 
-            rdtscll(tm->exit_time);
-            TM_DBG(info,INTR,"%lld exits happened, time delta is %lld",(info->num_exits - tm->entry_exits),(tm->entry_time - tm->exit_time));
-
             // We do indeed have pending interrupts
             v3_stgi();
-            TM_DBG(info,INTR,"we have a pending interrupt!\n");
 
-            v3_handle_trans_abort(info);
+            TM_DBG(info,INTR,"we have a pending interrupt\n");
+
+            v3_handle_trans_abort(info, TM_ABORT_UNSPECIFIED, 0);
+
             // Copy new RIP state into arch dependent structure
             guest_state->rip = info->rip;
-            TM_DBG(info,INTR,"currently guest state rip is %llx\n",(uint64_t)guest_state->rip);
+
+            //TM_DBG(info,INTR,"currently guest state rip is %llx\n",(uint64_t)guest_state->rip);
             v3_clgi();
         }
 
@@ -2283,3 +2351,4 @@ v3_tm_decode_rtm_instrs (struct guest_info * info,
     return 0;
 }
 
+