VirtualBox

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

Last change on this file since 93115 was 93115, checked in by vboxsync, 2 years ago

scm --update-copyright-year

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

© 2023 Oracle
ContactPrivacy policyTerms of Use