VirtualBox

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

Last change on this file since 106061 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

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