VirtualBox

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

最後變更 在這個檔案從80268是 80268,由 vboxsync 提交於 6 年 前

VMM: Refactoring VMMAll/* to use VMCC & VMMCPUCC. bugref:9217

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

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