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

     1  /*
     2   * Copyright (c) 2013 Intel Corporation
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *     http://www.apache.org/licenses/LICENSE-2.0
     8   * Unless required by applicable law or agreed to in writing, software
     9   * distributed under the License is distributed on an "AS IS" BASIS,
    10   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11   * See the License for the specific language governing permissions and
    12   * limitations under the License.
    13   */
    14  
    15  #include "file_codes.h"
    16  // need these defines for guest.h -> array_iterators.h
    17  #define VMM_DEADLOOP()          VMM_DEADLOOP_LOG(VMX_NMI_C)
    18  #define VMM_ASSERT(__condition) VMM_ASSERT_LOG(VMX_NMI_C, __condition)
    19  #include "vmm_defs.h"
    20  #include "vmm_dbg.h"
    21  #include "common_libc.h"
    22  #include "memory_allocator.h"
    23  #include "hw_utils.h"
    24  #include "isr.h"
    25  #include "vmm_objects.h"
    26  #include "guest.h"
    27  #include "guest_cpu.h"
    28  #include "guest_cpu_vmenter_event.h"
    29  #include "ipc.h"
    30  #include "vmexit.h"
    31  #include "vmx_ctrl_msrs.h"
    32  #include "vmx_nmi.h"
    33  #ifdef JLMDEBUG
    34  #include "jlmdebug.h"
    35  #endif
    36  
    37  
    38  /*
    39  +---------+---------+---------+---------+---------------+-----------+
    40  |  input  |  input  |  input  |output   |     output    |  output   |
    41  +---------+---------+---------+---------+---------------+-----------+
    42  |         |         | X-lated |X-lated  |               |           |
    43  | Reason  | Platform| Reason  |reason   | Local Action  |Deliver to |
    44  |         |   NMI   |         |requested|               | upper VMM |
    45  |         |         |         |by Lvl-1 |               |           |
    46  +---------+---------+---------+---------+---------------+-----------+
    47  |   NMI   |   No    |No reason|   N/A   |    Dismiss    |     No    |
    48  +---------+---------+---------+---------+---------------+-----------+
    49  |   NMI   |   Yes   |   NMI   |   No    |Inject to guest|     No    |
    50  +---------+---------+---------+---------+---------------+-----------+
    51  |   NMI   |   Yes   |   NMI   |   Yes   |Emulate x-lated|     Yes   |
    52  |         |         |         |         |vmexit to lvl-1|           |
    53  +---------+---------+---------+---------+---------------+-----------+
    54  | NMI-Win |   No    | NMI-Win |   No    |    Dismiss    |     No    |
    55  +---------+---------+---------+---------+---------------+-----------+
    56  | NMI-Win |   No    | NMI-Win |   Yes   |Emulate x-lated|     Yes   |
    57  |         |         |         |         |vmexit to lvl-1|           |
    58  +---------+---------+---------+---------+---------------+-----------+
    59  | NMI-Win |   Yes   |   NMI   |   No    |Inject to guest|     Yes   |
    60  +---------+---------+---------+---------+---------------+-----------+
    61  | NMI-Win |   Yes   |   NMI   |   Yes   |Emulate x-lated|     Yes   |
    62  |         |         |         |         |vmexit to lvl-1|           |
    63  +---------+---------+---------+---------+---------------+-----------+
    64  */
    65  
    66  
    67  
    68  #define XLAT_NMI_VMEXIT_REASON(__nmi_exists) (__nmi_exists) ?                  \
    69      Ia32VmxExitBasicReasonSoftwareInterruptExceptionNmi :                      \
    70      Ia32VmxExitBasicReasonCount
    71  
    72  #define XLAT_NMI_WINDOW_VMEXIT_REASON(__nmi_exists) (__nmi_exists) ?           \
    73      Ia32VmxExitBasicReasonSoftwareInterruptExceptionNmi :                      \
    74      Ia32VmxExitNmiWindow
    75  
    76  #define NMI_EXISTS_ON_GCPU(__gcpu)                                             \
    77      nmi_is_pending_this() && guest_is_nmi_owner(gcpu_guest_handle(__gcpu))
    78  
    79  
    80  #ifdef LEGACY_LAYERING
    81  #define COPY_VMCS1_HOST_TO_MERGE_GUEST()    // TODO:
    82  #else
    83  #define COPY_VMCS1_HOST_TO_MERGE_GUEST()    //
    84  #endif
    85  
    86  
    87  static BOOLEAN *nmi_array;
    88  
    89  VMEXIT_HANDLING_STATUS nmi_process_translated_reason(GUEST_CPU_HANDLE gcpu,
    90                                              IA32_VMX_EXIT_BASIC_REASON xlat_reason);
    91  static VMEXIT_HANDLING_STATUS nmi_propagate_nmi(GUEST_CPU_HANDLE gcpu);
    92  static VMEXIT_HANDLING_STATUS nmi_propagate_nmi_window(GUEST_CPU_HANDLE gcpu);
    93  static void nmi_emulate_nmi_vmexit(GUEST_CPU_HANDLE gcpu);
    94  static void nmi_emulate_nmi_window_vmexit(GUEST_CPU_HANDLE gcpu);
    95  static CPU_ID nmi_num_of_cores;
    96  
    97  
    98  BOOLEAN nmi_manager_initialize(CPU_ID num_of_cores)
    99  {
   100      BOOLEAN success = FALSE;
   101  
   102      do {
   103          nmi_array = vmm_malloc(num_of_cores * sizeof(BOOLEAN));
   104          nmi_num_of_cores=num_of_cores;
   105          if (NULL == nmi_array) {
   106              break;
   107          }
   108          vmm_memset(nmi_array, 0, num_of_cores * sizeof(BOOLEAN));
   109          success = ipc_initialize(num_of_cores);
   110      } while (0);
   111      // no need to release memory in case of failure, because it is Fatal
   112      return success;
   113  }
   114  
   115  
   116  //static void nmi_raise(CPU_ID cpu_id)
   117  void nmi_raise(CPU_ID cpu_id)
   118  {
   119      VMM_LOG(mask_anonymous, level_trace,"[nmi] Platform NMI on CPU%d\n", cpu_id);
   120      nmi_array[cpu_id] = TRUE;
   121  }
   122  
   123  //static void nmi_clear(CPU_ID cpu_id)
   124  void nmi_clear(CPU_ID cpu_id)
   125  {
   126      nmi_array[cpu_id] = FALSE;
   127  }
   128  
   129  //static BOOLEAN nmi_is_pending(CPU_ID cpu_id)
   130  BOOLEAN nmi_is_pending(CPU_ID cpu_id)
   131  {
   132      return nmi_array[cpu_id];
   133  }
   134  
   135  void nmi_raise_this(void)
   136  {
   137  	CPU_ID cpu_id=hw_cpu_id();
   138  	if(cpu_id>=nmi_num_of_cores) {
   139  		VMM_LOG(mask_anonymous, level_error, "Error: invalid cpu_id.\n");
   140  		return;
   141  	}
   142      nmi_raise(cpu_id);
   143  }
   144  
   145  void nmi_clear_this(void)
   146  {
   147      CPU_ID cpu_id=hw_cpu_id();
   148  
   149      if(cpu_id>=nmi_num_of_cores) {
   150          VMM_LOG(mask_anonymous, level_error, "Error: invalid cpu_id.\n");
   151          return;
   152      }
   153      nmi_clear(cpu_id);
   154  }
   155  
   156  BOOLEAN nmi_is_pending_this(void)
   157  {
   158      CPU_ID cpu_id=hw_cpu_id();
   159      if(cpu_id>=nmi_num_of_cores) {
   160          VMM_LOG(mask_anonymous, level_error, "Error: invalid cpu_id.\n");
   161          return FALSE;
   162      }
   163      return nmi_is_pending(cpu_id);
   164  }
   165  
   166  
   167  // FUNCTION : nmi_resume_handler()
   168  // PURPOSE  : If current CPU is platform NMI owner and unhandled platform NMI
   169  //          : exists on current CPU, sets NMI-Window to get VMEXIT asap.
   170  // ARGUMENTS: GUEST_CPU_HANDLE gcpu
   171  void nmi_resume_handler(GUEST_CPU_HANDLE gcpu)
   172  {
   173      if (NMI_EXISTS_ON_GCPU(gcpu)) {
   174          gcpu_set_pending_nmi(gcpu, TRUE);
   175      }
   176  }
   177  
   178  
   179  // FUNCTION : nmi_vmexit_handler()
   180  // PURPOSE  : Process NMI VMEXIT
   181  // ARGUMENTS: GUEST_CPU_HANDLE gcpu
   182  // RETURNS  : Status which says if VMEXIT was finally handled or
   183  //          : it should be processed by upper layer
   184  // CALLED   : called as bottom-up local handler
   185  VMEXIT_HANDLING_STATUS nmi_vmexit_handler(GUEST_CPU_HANDLE gcpu)
   186  {
   187      ipc_nmi_vmexit_handler(gcpu);
   188      return nmi_process_translated_reason(gcpu, XLAT_NMI_VMEXIT_REASON(NMI_EXISTS_ON_GCPU(gcpu)));
   189  }
   190  
   191  // FUNCTION : nmi_window_vmexit_handler()
   192  // PURPOSE  : Process NMI Window VMEXIT
   193  // ARGUMENTS: GUEST_CPU_HANDLE gcpu
   194  // RETURNS  : Status which says if VMEXIT was finally handled or
   195  //          : it should be processed by upper layer
   196  // CALLED   : called as bottom-up local handler
   197  VMEXIT_HANDLING_STATUS nmi_window_vmexit_handler(GUEST_CPU_HANDLE gcpu)
   198  {
   199      ipc_nmi_window_vmexit_handler(gcpu);
   200      gcpu_set_pending_nmi(gcpu, FALSE);  // TODO: substitute with new NMI-Window registration service
   201      return nmi_process_translated_reason(gcpu, XLAT_NMI_WINDOW_VMEXIT_REASON(NMI_EXISTS_ON_GCPU(gcpu)));
   202  }
   203  
   204  VMEXIT_HANDLING_STATUS nmi_process_translated_reason(
   205      GUEST_CPU_HANDLE           gcpu,
   206      IA32_VMX_EXIT_BASIC_REASON xlat_reason)
   207  {
   208      VMEXIT_HANDLING_STATUS status;
   209  
   210      switch (xlat_reason) {
   211      case Ia32VmxExitBasicReasonSoftwareInterruptExceptionNmi:
   212          status = nmi_propagate_nmi(gcpu);
   213          break;
   214      case Ia32VmxExitNmiWindow:
   215          status = nmi_propagate_nmi_window(gcpu);
   216          break;
   217      default:
   218          status = VMEXIT_HANDLED;    // dismiss
   219          break;
   220      }
   221      return status;
   222  }
   223  
   224  
   225  // FUNCTION : nmi_propagate_nmi()
   226  // PURPOSE  : If layered and upper VMM requested NMI VMEXIT, emulate it,
   227  //          : else inject it directly to VM
   228  // ARGUMENTS: GUEST_CPU_HANDLE gcpu
   229  // RETURNS  : Status which says if VMEXIT was finally handled or
   230  //          : it should be processed by upper layer
   231  VMEXIT_HANDLING_STATUS nmi_propagate_nmi(GUEST_CPU_HANDLE gcpu)
   232  {
   233      VMEXIT_HANDLING_STATUS status;
   234  
   235      do {
   236          if (gcpu_is_vmcs_layered(gcpu)) {
   237              // if upper layer requested NMI VMEXIT, emulate NMI VMEXIT into it
   238              VMCS_OBJECT *vmcs1 = gcpu_get_vmcs_layered(gcpu, VMCS_LEVEL_1);
   239              PIN_BASED_VM_EXECUTION_CONTROLS pin_based_vmexit_ctrls;
   240              pin_based_vmexit_ctrls.Uint32 = (UINT32) vmcs_read(vmcs1, VMCS_CONTROL_VECTOR_PIN_EVENTS);
   241              if (pin_based_vmexit_ctrls.Bits.Nmi) {
   242                  nmi_emulate_nmi_vmexit(gcpu);
   243                  nmi_clear_this();
   244                  status = VMEXIT_NOT_HANDLED;
   245                  break;
   246              }
   247          }
   248          // here is non-layered case, or level.1 did not request NMI VMEXIT
   249          if (gcpu_inject_nmi(gcpu)) {
   250              nmi_clear_this();
   251          }
   252          // do not deliver to upper level even if NMI was not really injected
   253          status = VMEXIT_HANDLED;
   254      } while (0);
   255      return status;
   256  }
   257  
   258  
   259  // FUNCTION : nmi_propagate_nmi_window()
   260  // PURPOSE  : If layered and upper VMM requested NMI-Window VMEXIT, emulate it,
   261  //          : else dismiss it.
   262  // ARGUMENTS: GUEST_CPU_HANDLE gcpu
   263  // RETURNS  : Status which says if VMEXIT was finally handled or
   264  //          : it should be processed by upper layer
   265  VMEXIT_HANDLING_STATUS nmi_propagate_nmi_window(GUEST_CPU_HANDLE gcpu)
   266  {
   267      VMEXIT_HANDLING_STATUS status;
   268  
   269      do {
   270          if (gcpu_is_vmcs_layered(gcpu)) {
   271              // if upper layer requested NMI VMEXIT, emulate NMI VMEXIT into it
   272              VMCS_OBJECT *vmcs1 = gcpu_get_vmcs_layered(gcpu, VMCS_LEVEL_1);
   273              PROCESSOR_BASED_VM_EXECUTION_CONTROLS ctrls;
   274  
   275              ctrls.Uint32 = (UINT32)vmcs_read(vmcs1, VMCS_CONTROL_VECTOR_PROCESSOR_EVENTS);
   276              if (ctrls.Bits.NmiWindow) {
   277                  nmi_emulate_nmi_window_vmexit(gcpu);
   278                  status = VMEXIT_NOT_HANDLED;
   279                  break;
   280              }
   281          }
   282  
   283          // here is non-layered case, or level.1 did not request NMI Window VMEXIT
   284          // do not deliver NMI Window to upper level
   285          status = VMEXIT_HANDLED;
   286      } while (0);
   287  
   288      return status;
   289  }
   290  
   291  
   292  // FUNCTION : nmi_emulate_nmi_vmexit()
   293  // PURPOSE  : Emulates NMI VMEXIT into upper VMM
   294  // ARGUMENTS: GUEST_CPU_HANDLE gcpu
   295  void nmi_emulate_nmi_vmexit(GUEST_CPU_HANDLE gcpu)
   296  {
   297      VMCS_OBJECT *vmcs = gcpu_get_vmcs_layered(gcpu, VMCS_MERGED);
   298      UINT32  reason;
   299      IA32_VMX_VMCS_VM_EXIT_INFO_INTERRUPT_INFO exception_info;
   300  
   301      (void)reason;
   302      VMM_CALLTRACE_ENTER();
   303      reason = (UINT32)vmcs_read(vmcs, VMCS_EXIT_INFO_REASON);
   304  
   305      // change VMEXIT INFO, which is read-only. It is done in cache only
   306      // and should not be writeen to hardware VMCS
   307      vmcs_write_nocheck(vmcs, VMCS_EXIT_INFO_REASON,
   308          (UINT64)Ia32VmxExitBasicReasonSoftwareInterruptExceptionNmi);
   309  
   310      exception_info.Uint32 = 0;
   311      exception_info.Bits.Vector        = IA32_EXCEPTION_VECTOR_NMI;
   312      exception_info.Bits.InterruptType = IA32_EXCEPTION_VECTOR_NMI;
   313      exception_info.Bits.Valid         = 1;
   314      vmcs_write_nocheck(vmcs, VMCS_EXIT_INFO_EXCEPTION_INFO, (UINT64)exception_info.Uint32);
   315      COPY_VMCS1_HOST_TO_MERGE_GUEST();
   316      VMM_CALLTRACE_LEAVE();
   317  }
   318  
   319  
   320  // FUNCTION : nmi_emulate_nmi_window_vmexit()
   321  // PURPOSE  : Emulates NMI-Window VMEXIT into upper VMM
   322  // ARGUMENTS: GUEST_CPU_HANDLE gcpu
   323  #pragma warning(disable : 4100)
   324  void nmi_emulate_nmi_window_vmexit(GUEST_CPU_HANDLE gcpu UNUSED)
   325  {
   326      VMM_CALLTRACE_ENTER();
   327      COPY_VMCS1_HOST_TO_MERGE_GUEST();
   328      VMM_CALLTRACE_LEAVE();
   329  }
   330  #pragma warning(default : 4100)
   331