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