VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/GIMAll.cpp@ 74065

Last change on this file since 74065 was 72469, checked in by vboxsync, 6 years ago

GIM,IEM: Correctly hook up hypercalls thru IEM. bugref:9044

  • IEM: Pass opcode and instruction length to GIM so it can do patching.
  • GIM: Introduced GIMHypercallEx API for receiving hypercalls with instruction opcode+length. Hooking this into the exiting #UD code paths.
  • GIM: Move the VMMPatchHypercall API into GIM and corrected the name to GIMQueryHypercallOpcodeBytes.
  • GIM/KVM: Use GIMQueryHypercallOpcodeBytes to decide which instruction is native and cache the opcode bytes for patching.
  • GIM/KVM: Check the VMCALL instruction encoding length rather than assuming its always 3 bytes when patching.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.7 KB
Line 
1/* $Id: GIMAll.cpp 72469 2018-06-07 11:35:23Z vboxsync $ */
2/** @file
3 * GIM - Guest Interface Manager - All Contexts.
4 */
5
6/*
7 * Copyright (C) 2014-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_GIM
23#include <VBox/vmm/gim.h>
24#include <VBox/vmm/em.h> /* For EMInterpretDisasCurrent */
25#include "GIMInternal.h"
26#include <VBox/vmm/vm.h>
27
28#include <VBox/dis.h> /* For DISCPUSTATE */
29#include <VBox/err.h>
30
31/* Include all the providers. */
32#include "GIMHvInternal.h"
33#include "GIMMinimalInternal.h"
34
35
36/**
37 * Checks whether GIM is being used by this VM.
38 *
39 * @retval true if used.
40 * @retval false if no GIM provider ("none") is used.
41 *
42 * @param pVM The cross context VM structure.
43 */
44VMMDECL(bool) GIMIsEnabled(PVM pVM)
45{
46 return pVM->gim.s.enmProviderId != GIMPROVIDERID_NONE;
47}
48
49
50/**
51 * Gets the GIM provider configured for this VM.
52 *
53 * @returns The GIM provider Id.
54 * @param pVM The cross context VM structure.
55 */
56VMMDECL(GIMPROVIDERID) GIMGetProvider(PVM pVM)
57{
58 return pVM->gim.s.enmProviderId;
59}
60
61
62/**
63 * Returns whether the guest has configured and enabled calls to the hypervisor.
64 *
65 * @returns true if hypercalls are enabled and usable, false otherwise.
66 * @param pVCpu The cross context virtual CPU structure.
67 */
68VMM_INT_DECL(bool) GIMAreHypercallsEnabled(PVMCPU pVCpu)
69{
70 PVM pVM = pVCpu->CTX_SUFF(pVM);
71 if (!GIMIsEnabled(pVM))
72 return false;
73
74 switch (pVM->gim.s.enmProviderId)
75 {
76 case GIMPROVIDERID_HYPERV:
77 return gimHvAreHypercallsEnabled(pVCpu);
78
79 case GIMPROVIDERID_KVM:
80 return gimKvmAreHypercallsEnabled(pVCpu);
81
82 default:
83 return false;
84 }
85}
86
87
88/**
89 * Implements a GIM hypercall with the provider configured for the VM.
90 *
91 * @returns Strict VBox status code.
92 * @retval VINF_SUCCESS if the hypercall succeeded (even if its operation
93 * failed).
94 * @retval VINF_GIM_HYPERCALL_CONTINUING continue hypercall without updating
95 * RIP.
96 * @retval VINF_GIM_R3_HYPERCALL re-start the hypercall from ring-3.
97 * @retval VERR_GIM_HYPERCALL_ACCESS_DENIED CPL is insufficient.
98 * @retval VERR_GIM_HYPERCALLS_NOT_AVAILABLE hypercalls unavailable.
99 * @retval VERR_GIM_NOT_ENABLED GIM is not enabled (shouldn't really happen)
100 * @retval VERR_GIM_HYPERCALL_MEMORY_READ_FAILED hypercall failed while reading
101 * memory.
102 * @retval VERR_GIM_HYPERCALL_MEMORY_WRITE_FAILED hypercall failed while
103 * writing memory.
104 *
105 * @param pVCpu The cross context virtual CPU structure.
106 * @param pCtx Pointer to the guest-CPU context.
107 *
108 * @remarks The caller of this function needs to advance RIP as required.
109 * @thread EMT.
110 */
111VMM_INT_DECL(VBOXSTRICTRC) GIMHypercall(PVMCPU pVCpu, PCPUMCTX pCtx)
112{
113 PVM pVM = pVCpu->CTX_SUFF(pVM);
114 VMCPU_ASSERT_EMT(pVCpu);
115
116 if (RT_UNLIKELY(!GIMIsEnabled(pVM)))
117 return VERR_GIM_NOT_ENABLED;
118
119 switch (pVM->gim.s.enmProviderId)
120 {
121 case GIMPROVIDERID_HYPERV:
122 return gimHvHypercall(pVCpu, pCtx);
123
124 case GIMPROVIDERID_KVM:
125 return gimKvmHypercall(pVCpu, pCtx);
126
127 default:
128 AssertMsgFailed(("GIMHypercall: for provider %u not available/implemented\n", pVM->gim.s.enmProviderId));
129 return VERR_GIM_HYPERCALLS_NOT_AVAILABLE;
130 }
131}
132
133
134/**
135 * Same as GIMHypercall, except with disassembler opcode and instruction length.
136 *
137 * This is the interface used by IEM.
138 *
139 * @returns Strict VBox status code.
140 * @retval VINF_SUCCESS if the hypercall succeeded (even if its operation
141 * failed).
142 * @retval VINF_GIM_HYPERCALL_CONTINUING continue hypercall without updating
143 * RIP.
144 * @retval VINF_GIM_R3_HYPERCALL re-start the hypercall from ring-3.
145 * @retval VERR_GIM_HYPERCALL_ACCESS_DENIED CPL is insufficient.
146 * @retval VERR_GIM_HYPERCALLS_NOT_AVAILABLE hypercalls unavailable.
147 * @retval VERR_GIM_NOT_ENABLED GIM is not enabled (shouldn't really happen)
148 * @retval VERR_GIM_HYPERCALL_MEMORY_READ_FAILED hypercall failed while reading
149 * memory.
150 * @retval VERR_GIM_HYPERCALL_MEMORY_WRITE_FAILED hypercall failed while
151 * writing memory.
152 * @retval VERR_GIM_INVALID_HYPERCALL_INSTR if uDisOpcode is the wrong one; raise \#UD.
153 *
154 * @param pVCpu The cross context virtual CPU structure.
155 * @param pCtx Pointer to the guest-CPU context.
156 * @param uDisOpcode The disassembler opcode.
157 * @param cbInstr The instruction length.
158 *
159 * @remarks The caller of this function needs to advance RIP as required.
160 * @thread EMT.
161 */
162VMM_INT_DECL(VBOXSTRICTRC) GIMHypercallEx(PVMCPU pVCpu, PCPUMCTX pCtx, unsigned uDisOpcode, uint8_t cbInstr)
163{
164 PVM pVM = pVCpu->CTX_SUFF(pVM);
165 VMCPU_ASSERT_EMT(pVCpu);
166
167 if (RT_UNLIKELY(!GIMIsEnabled(pVM)))
168 return VERR_GIM_NOT_ENABLED;
169
170 switch (pVM->gim.s.enmProviderId)
171 {
172 case GIMPROVIDERID_HYPERV:
173 return gimHvHypercallEx(pVCpu, pCtx, uDisOpcode, cbInstr);
174
175 case GIMPROVIDERID_KVM:
176 return gimKvmHypercallEx(pVCpu, pCtx, uDisOpcode, cbInstr);
177
178 default:
179 AssertMsgFailedReturn(("enmProviderId=%u\n", pVM->gim.s.enmProviderId), VERR_GIM_HYPERCALLS_NOT_AVAILABLE);
180 }
181}
182
183
184/**
185 * Disassembles the instruction at RIP and if it's a hypercall
186 * instruction, performs the hypercall.
187 *
188 * @param pVCpu The cross context virtual CPU structure.
189 * @param pCtx Pointer to the guest-CPU context.
190 * @param pcbInstr Where to store the disassembled instruction length.
191 * Optional, can be NULL.
192 *
193 * @todo This interface should disappear when IEM/REM execution engines
194 * handle VMCALL/VMMCALL instructions to call into GIM when
195 * required. See @bugref{7270#c168}.
196 */
197VMM_INT_DECL(VBOXSTRICTRC) GIMExecHypercallInstr(PVMCPU pVCpu, PCPUMCTX pCtx, uint8_t *pcbInstr)
198{
199 PVM pVM = pVCpu->CTX_SUFF(pVM);
200 VMCPU_ASSERT_EMT(pVCpu);
201
202 if (RT_UNLIKELY(!GIMIsEnabled(pVM)))
203 return VERR_GIM_NOT_ENABLED;
204
205 unsigned cbInstr;
206 DISCPUSTATE Dis;
207 int rc = EMInterpretDisasCurrent(pVM, pVCpu, &Dis, &cbInstr);
208 if (RT_SUCCESS(rc))
209 {
210 if (pcbInstr)
211 *pcbInstr = (uint8_t)cbInstr;
212 switch (pVM->gim.s.enmProviderId)
213 {
214 case GIMPROVIDERID_HYPERV:
215 return gimHvHypercallEx(pVCpu, pCtx, Dis.pCurInstr->uOpcode, Dis.cbInstr);
216
217 case GIMPROVIDERID_KVM:
218 return gimKvmHypercallEx(pVCpu, pCtx, Dis.pCurInstr->uOpcode, Dis.cbInstr);
219
220 default:
221 AssertMsgFailed(("GIMExecHypercallInstr: for provider %u not available/implemented\n", pVM->gim.s.enmProviderId));
222 return VERR_GIM_HYPERCALLS_NOT_AVAILABLE;
223 }
224 }
225
226 Log(("GIM: GIMExecHypercallInstr: Failed to disassemble CS:RIP=%04x:%08RX64. rc=%Rrc\n", pCtx->cs.Sel, pCtx->rip, rc));
227 return rc;
228}
229
230
231/**
232 * Returns whether the guest has configured and setup the use of paravirtualized
233 * TSC.
234 *
235 * Paravirtualized TSCs are per-VM and the rest of the execution engine logic
236 * relies on that.
237 *
238 * @returns true if enabled and usable, false otherwise.
239 * @param pVM The cross context VM structure.
240 */
241VMM_INT_DECL(bool) GIMIsParavirtTscEnabled(PVM pVM)
242{
243 switch (pVM->gim.s.enmProviderId)
244 {
245 case GIMPROVIDERID_HYPERV:
246 return gimHvIsParavirtTscEnabled(pVM);
247
248 case GIMPROVIDERID_KVM:
249 return gimKvmIsParavirtTscEnabled(pVM);
250
251 default:
252 break;
253 }
254 return false;
255}
256
257
258/**
259 * Whether \#UD exceptions in the guest needs to be intercepted by the GIM
260 * provider.
261 *
262 * At the moment, the reason why this isn't a more generic interface wrt to
263 * exceptions is because of performance (each VM-exit would have to manually
264 * check whether or not GIM needs to be notified). Left as a todo for later if
265 * really required.
266 *
267 * @returns true if needed, false otherwise.
268 * @param pVCpu The cross context virtual CPU structure.
269 */
270VMM_INT_DECL(bool) GIMShouldTrapXcptUD(PVMCPU pVCpu)
271{
272 PVM pVM = pVCpu->CTX_SUFF(pVM);
273 if (!GIMIsEnabled(pVM))
274 return false;
275
276 switch (pVM->gim.s.enmProviderId)
277 {
278 case GIMPROVIDERID_KVM:
279 return gimKvmShouldTrapXcptUD(pVCpu);
280
281 case GIMPROVIDERID_HYPERV:
282 return gimHvShouldTrapXcptUD(pVCpu);
283
284 default:
285 return false;
286 }
287}
288
289
290/**
291 * Exception handler for \#UD when requested by the GIM provider.
292 *
293 * @returns Strict VBox status code.
294 * @retval VINF_SUCCESS if the hypercall succeeded (even if its operation
295 * failed).
296 * @retval VINF_GIM_R3_HYPERCALL restart the hypercall from ring-3.
297 * @retval VINF_GIM_HYPERCALL_CONTINUING continue hypercall without updating
298 * RIP.
299 * @retval VERR_GIM_HYPERCALL_ACCESS_DENIED CPL is insufficient.
300 * @retval VERR_GIM_INVALID_HYPERCALL_INSTR instruction at RIP is not a valid
301 * hypercall instruction.
302 *
303 * @param pVCpu The cross context virtual CPU structure.
304 * @param pCtx Pointer to the guest-CPU context.
305 * @param pDis Pointer to the disassembled instruction state at RIP.
306 * If NULL is passed, it implies the disassembly of the
307 * the instruction at RIP is the responsibility of the
308 * GIM provider.
309 * @param pcbInstr Where to store the instruction length of the hypercall
310 * instruction. Optional, can be NULL.
311 *
312 * @thread EMT(pVCpu).
313 */
314VMM_INT_DECL(VBOXSTRICTRC) GIMXcptUD(PVMCPU pVCpu, PCPUMCTX pCtx, PDISCPUSTATE pDis, uint8_t *pcbInstr)
315{
316 PVM pVM = pVCpu->CTX_SUFF(pVM);
317 Assert(GIMIsEnabled(pVM));
318 Assert(pDis || pcbInstr);
319
320 switch (pVM->gim.s.enmProviderId)
321 {
322 case GIMPROVIDERID_KVM:
323 return gimKvmXcptUD(pVCpu, pCtx, pDis, pcbInstr);
324
325 case GIMPROVIDERID_HYPERV:
326 return gimHvXcptUD(pVCpu, pCtx, pDis, pcbInstr);
327
328 default:
329 return VERR_GIM_OPERATION_FAILED;
330 }
331}
332
333
334/**
335 * Invokes the read-MSR handler for the GIM provider configured for the VM.
336 *
337 * @returns Strict VBox status code like CPUMQueryGuestMsr.
338 * @retval VINF_CPUM_R3_MSR_READ
339 * @retval VERR_CPUM_RAISE_GP_0
340 *
341 * @param pVCpu The cross context virtual CPU structure.
342 * @param idMsr The MSR to read.
343 * @param pRange The range this MSR belongs to.
344 * @param puValue Where to store the MSR value read.
345 */
346VMM_INT_DECL(VBOXSTRICTRC) GIMReadMsr(PVMCPU pVCpu, uint32_t idMsr, PCCPUMMSRRANGE pRange, uint64_t *puValue)
347{
348 Assert(pVCpu);
349 PVM pVM = pVCpu->CTX_SUFF(pVM);
350 Assert(GIMIsEnabled(pVM));
351 VMCPU_ASSERT_EMT(pVCpu);
352
353 switch (pVM->gim.s.enmProviderId)
354 {
355 case GIMPROVIDERID_HYPERV:
356 return gimHvReadMsr(pVCpu, idMsr, pRange, puValue);
357
358 case GIMPROVIDERID_KVM:
359 return gimKvmReadMsr(pVCpu, idMsr, pRange, puValue);
360
361 default:
362 AssertMsgFailed(("GIMReadMsr: for unknown provider %u idMsr=%#RX32 -> #GP(0)", pVM->gim.s.enmProviderId, idMsr));
363 return VERR_CPUM_RAISE_GP_0;
364 }
365}
366
367
368/**
369 * Invokes the write-MSR handler for the GIM provider configured for the VM.
370 *
371 * @returns Strict VBox status code like CPUMSetGuestMsr.
372 * @retval VINF_CPUM_R3_MSR_WRITE
373 * @retval VERR_CPUM_RAISE_GP_0
374 *
375 * @param pVCpu The cross context virtual CPU structure.
376 * @param idMsr The MSR to write.
377 * @param pRange The range this MSR belongs to.
378 * @param uValue The value to set, ignored bits masked.
379 * @param uRawValue The raw value with the ignored bits not masked.
380 */
381VMM_INT_DECL(VBOXSTRICTRC) GIMWriteMsr(PVMCPU pVCpu, uint32_t idMsr, PCCPUMMSRRANGE pRange, uint64_t uValue, uint64_t uRawValue)
382{
383 AssertPtr(pVCpu);
384 NOREF(uValue);
385
386 PVM pVM = pVCpu->CTX_SUFF(pVM);
387 Assert(GIMIsEnabled(pVM));
388 VMCPU_ASSERT_EMT(pVCpu);
389
390 switch (pVM->gim.s.enmProviderId)
391 {
392 case GIMPROVIDERID_HYPERV:
393 return gimHvWriteMsr(pVCpu, idMsr, pRange, uRawValue);
394
395 case GIMPROVIDERID_KVM:
396 return gimKvmWriteMsr(pVCpu, idMsr, pRange, uRawValue);
397
398 default:
399 AssertMsgFailed(("GIMWriteMsr: for unknown provider %u idMsr=%#RX32 -> #GP(0)", pVM->gim.s.enmProviderId, idMsr));
400 return VERR_CPUM_RAISE_GP_0;
401 }
402}
403
404
405/**
406 * Queries the opcode bytes for a native hypercall.
407 *
408 * @returns VBox status code.
409 * @param pVM The cross context VM structure.
410 * @param pvBuf The destination buffer.
411 * @param cbBuf The size of the buffer.
412 * @param pcbWritten Where to return the number of bytes written. This is
413 * reliably updated only on successful return. Optional.
414 * @param puDisOpcode Where to return the disassembler opcode. Optional.
415 */
416VMM_INT_DECL(int) GIMQueryHypercallOpcodeBytes(PVM pVM, void *pvBuf, size_t cbBuf, size_t *pcbWritten, uint16_t *puDisOpcode)
417{
418 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
419
420 CPUMCPUVENDOR enmHostCpu = CPUMGetHostCpuVendor(pVM);
421 uint8_t const *pbSrc;
422 size_t cbSrc;
423 switch (enmHostCpu)
424 {
425 case CPUMCPUVENDOR_AMD:
426 {
427 if (puDisOpcode)
428 *puDisOpcode = OP_VMMCALL;
429 static uint8_t const s_abHypercall[] = { 0x0F, 0x01, 0xD9 }; /* VMMCALL */
430 pbSrc = s_abHypercall;
431 cbSrc = sizeof(s_abHypercall);
432 break;
433 }
434
435 case CPUMCPUVENDOR_INTEL:
436 case CPUMCPUVENDOR_VIA:
437 {
438 if (puDisOpcode)
439 *puDisOpcode = OP_VMCALL;
440 static uint8_t const s_abHypercall[] = { 0x0F, 0x01, 0xC1 }; /* VMCALL */
441 pbSrc = s_abHypercall;
442 cbSrc = sizeof(s_abHypercall);
443 break;
444 }
445
446 default:
447 AssertMsgFailedReturn(("%d\n", enmHostCpu), VERR_UNSUPPORTED_CPU);
448 }
449 if (RT_LIKELY(cbBuf >= cbSrc))
450 {
451 memcpy(pvBuf, pbSrc, cbSrc);
452 if (pcbWritten)
453 *pcbWritten = cbSrc;
454 return VINF_SUCCESS;
455 }
456 return VERR_BUFFER_OVERFLOW;
457}
458
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette