VirtualBox

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

Last change on this file since 100088 was 99208, checked in by vboxsync, 20 months ago

Disassembler,VMM,Runtime: Get rid of deprecated DISCPUSTATE types (preparation for architecture specific separation in order to support ARMv8), bugref:10394

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.5 KB
Line 
1/* $Id: GIMAll.cpp 99208 2023-03-29 14:13:56Z vboxsync $ */
2/** @file
3 * GIM - Guest Interface Manager - All Contexts.
4 */
5
6/*
7 * Copyright (C) 2014-2023 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 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
158 VMCPU_ASSERT_EMT(pVCpu);
159
160 if (RT_UNLIKELY(!GIMIsEnabled(pVM)))
161 return VERR_GIM_NOT_ENABLED;
162
163 switch (pVM->gim.s.enmProviderId)
164 {
165#if !defined(VBOX_VMM_TARGET_ARMV8)
166 case GIMPROVIDERID_HYPERV:
167 return gimHvHypercall(pVCpu, pCtx);
168
169 case GIMPROVIDERID_KVM:
170 return gimKvmHypercall(pVCpu, pCtx);
171#endif
172 default:
173 AssertMsgFailed(("GIMHypercall: for provider %u not available/implemented\n", pVM->gim.s.enmProviderId));
174 return VERR_GIM_HYPERCALLS_NOT_AVAILABLE;
175 }
176}
177
178
179/**
180 * Same as GIMHypercall, except with disassembler opcode and instruction length.
181 *
182 * This is the interface used by IEM.
183 *
184 * @returns Strict VBox status code.
185 * @retval VINF_SUCCESS if the hypercall succeeded (even if its operation
186 * failed).
187 * @retval VINF_GIM_HYPERCALL_CONTINUING continue hypercall without updating
188 * RIP.
189 * @retval VINF_GIM_R3_HYPERCALL re-start the hypercall from ring-3.
190 * @retval VERR_GIM_HYPERCALL_ACCESS_DENIED CPL is insufficient.
191 * @retval VERR_GIM_HYPERCALLS_NOT_AVAILABLE hypercalls unavailable.
192 * @retval VERR_GIM_NOT_ENABLED GIM is not enabled (shouldn't really happen)
193 * @retval VERR_GIM_HYPERCALL_MEMORY_READ_FAILED hypercall failed while reading
194 * memory.
195 * @retval VERR_GIM_HYPERCALL_MEMORY_WRITE_FAILED hypercall failed while
196 * writing memory.
197 * @retval VERR_GIM_INVALID_HYPERCALL_INSTR if uDisOpcode is the wrong one; raise \#UD.
198 *
199 * @param pVCpu The cross context virtual CPU structure.
200 * @param pCtx Pointer to the guest-CPU context.
201 * @param uDisOpcode The disassembler opcode.
202 * @param cbInstr The instruction length.
203 *
204 * @remarks The caller of this function needs to advance RIP as required.
205 * @thread EMT.
206 */
207VMM_INT_DECL(VBOXSTRICTRC) GIMHypercallEx(PVMCPUCC pVCpu, PCPUMCTX pCtx, unsigned uDisOpcode, uint8_t cbInstr)
208{
209 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
210 VMCPU_ASSERT_EMT(pVCpu);
211
212 if (RT_UNLIKELY(!GIMIsEnabled(pVM)))
213 return VERR_GIM_NOT_ENABLED;
214
215 switch (pVM->gim.s.enmProviderId)
216 {
217#if !defined(VBOX_VMM_TARGET_ARMV8)
218 case GIMPROVIDERID_HYPERV:
219 return gimHvHypercallEx(pVCpu, pCtx, uDisOpcode, cbInstr);
220
221 case GIMPROVIDERID_KVM:
222 return gimKvmHypercallEx(pVCpu, pCtx, uDisOpcode, cbInstr);
223#endif
224 default:
225 AssertMsgFailedReturn(("enmProviderId=%u\n", pVM->gim.s.enmProviderId), VERR_GIM_HYPERCALLS_NOT_AVAILABLE);
226 }
227}
228
229
230/**
231 * Disassembles the instruction at RIP and if it's a hypercall
232 * instruction, performs the hypercall.
233 *
234 * @param pVCpu The cross context virtual CPU structure.
235 * @param pCtx Pointer to the guest-CPU context.
236 * @param pcbInstr Where to store the disassembled instruction length.
237 * Optional, can be NULL.
238 *
239 * @todo This interface should disappear when IEM/REM execution engines
240 * handle VMCALL/VMMCALL instructions to call into GIM when
241 * required. See @bugref{7270#c168}.
242 */
243VMM_INT_DECL(VBOXSTRICTRC) GIMExecHypercallInstr(PVMCPUCC pVCpu, PCPUMCTX pCtx, uint8_t *pcbInstr)
244{
245 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
246 VMCPU_ASSERT_EMT(pVCpu);
247
248 if (RT_UNLIKELY(!GIMIsEnabled(pVM)))
249 return VERR_GIM_NOT_ENABLED;
250
251 unsigned cbInstr;
252 DISSTATE Dis;
253 int rc = EMInterpretDisasCurrent(pVCpu, &Dis, &cbInstr);
254 if (RT_SUCCESS(rc))
255 {
256 if (pcbInstr)
257 *pcbInstr = (uint8_t)cbInstr;
258 switch (pVM->gim.s.enmProviderId)
259 {
260#if !defined(VBOX_VMM_TARGET_ARMV8)
261 case GIMPROVIDERID_HYPERV:
262 return gimHvHypercallEx(pVCpu, pCtx, Dis.pCurInstr->uOpcode, Dis.cbInstr);
263
264 case GIMPROVIDERID_KVM:
265 return gimKvmHypercallEx(pVCpu, pCtx, Dis.pCurInstr->uOpcode, Dis.cbInstr);
266#endif
267 default:
268 AssertMsgFailed(("GIMExecHypercallInstr: for provider %u not available/implemented\n", pVM->gim.s.enmProviderId));
269 return VERR_GIM_HYPERCALLS_NOT_AVAILABLE;
270 }
271 }
272
273#if !defined(VBOX_VMM_TARGET_ARMV8)
274 Log(("GIM: GIMExecHypercallInstr: Failed to disassemble CS:RIP=%04x:%08RX64. rc=%Rrc\n", pCtx->cs.Sel, pCtx->rip, rc));
275#endif
276 return rc;
277}
278
279
280/**
281 * Returns whether the guest has configured and setup the use of paravirtualized
282 * TSC.
283 *
284 * Paravirtualized TSCs are per-VM and the rest of the execution engine logic
285 * relies on that.
286 *
287 * @returns true if enabled and usable, false otherwise.
288 * @param pVM The cross context VM structure.
289 */
290VMM_INT_DECL(bool) GIMIsParavirtTscEnabled(PVMCC pVM)
291{
292 switch (pVM->gim.s.enmProviderId)
293 {
294#if !defined(VBOX_VMM_TARGET_ARMV8)
295 case GIMPROVIDERID_HYPERV:
296 return gimHvIsParavirtTscEnabled(pVM);
297
298 case GIMPROVIDERID_KVM:
299 return gimKvmIsParavirtTscEnabled(pVM);
300#endif
301 default:
302 break;
303 }
304 return false;
305}
306
307
308/**
309 * Whether \#UD exceptions in the guest needs to be intercepted by the GIM
310 * provider.
311 *
312 * At the moment, the reason why this isn't a more generic interface wrt to
313 * exceptions is because of performance (each VM-exit would have to manually
314 * check whether or not GIM needs to be notified). Left as a todo for later if
315 * really required.
316 *
317 * @returns true if needed, false otherwise.
318 * @param pVCpu The cross context virtual CPU structure.
319 */
320VMM_INT_DECL(bool) GIMShouldTrapXcptUD(PVMCPUCC pVCpu)
321{
322 PVM pVM = pVCpu->CTX_SUFF(pVM);
323 if (!GIMIsEnabled(pVM))
324 return false;
325
326 switch (pVM->gim.s.enmProviderId)
327 {
328#if !defined(VBOX_VMM_TARGET_ARMV8)
329 case GIMPROVIDERID_KVM:
330 return gimKvmShouldTrapXcptUD(pVM);
331
332 case GIMPROVIDERID_HYPERV:
333 return gimHvShouldTrapXcptUD(pVCpu);
334#endif
335 default:
336 return false;
337 }
338}
339
340
341/**
342 * Exception handler for \#UD when requested by the GIM provider.
343 *
344 * @returns Strict VBox status code.
345 * @retval VINF_SUCCESS if the hypercall succeeded (even if its operation
346 * failed).
347 * @retval VINF_GIM_R3_HYPERCALL restart the hypercall from ring-3.
348 * @retval VINF_GIM_HYPERCALL_CONTINUING continue hypercall without updating
349 * RIP.
350 * @retval VERR_GIM_HYPERCALL_ACCESS_DENIED CPL is insufficient.
351 * @retval VERR_GIM_INVALID_HYPERCALL_INSTR instruction at RIP is not a valid
352 * hypercall instruction.
353 *
354 * @param pVCpu The cross context virtual CPU structure.
355 * @param pCtx Pointer to the guest-CPU context.
356 * @param pDis Pointer to the disassembled instruction state at RIP.
357 * If NULL is passed, it implies the disassembly of the
358 * the instruction at RIP is the responsibility of the
359 * GIM provider.
360 * @param pcbInstr Where to store the instruction length of the hypercall
361 * instruction. Optional, can be NULL.
362 *
363 * @thread EMT(pVCpu).
364 */
365VMM_INT_DECL(VBOXSTRICTRC) GIMXcptUD(PVMCPUCC pVCpu, PCPUMCTX pCtx, PDISSTATE pDis, uint8_t *pcbInstr)
366{
367 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
368 Assert(GIMIsEnabled(pVM));
369 Assert(pDis || pcbInstr);
370
371 switch (pVM->gim.s.enmProviderId)
372 {
373#if !defined(VBOX_VMM_TARGET_ARMV8)
374 case GIMPROVIDERID_KVM:
375 return gimKvmXcptUD(pVM, pVCpu, pCtx, pDis, pcbInstr);
376
377 case GIMPROVIDERID_HYPERV:
378 return gimHvXcptUD(pVCpu, pCtx, pDis, pcbInstr);
379#endif
380 default:
381 return VERR_GIM_OPERATION_FAILED;
382 }
383}
384
385
386#if !defined(VBOX_VMM_TARGET_ARMV8)
387/**
388 * Invokes the read-MSR handler for the GIM provider configured for the VM.
389 *
390 * @returns Strict VBox status code like CPUMQueryGuestMsr.
391 * @retval VINF_CPUM_R3_MSR_READ
392 * @retval VERR_CPUM_RAISE_GP_0
393 *
394 * @param pVCpu The cross context virtual CPU structure.
395 * @param idMsr The MSR to read.
396 * @param pRange The range this MSR belongs to.
397 * @param puValue Where to store the MSR value read.
398 */
399VMM_INT_DECL(VBOXSTRICTRC) GIMReadMsr(PVMCPUCC pVCpu, uint32_t idMsr, PCCPUMMSRRANGE pRange, uint64_t *puValue)
400{
401 Assert(pVCpu);
402 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
403 Assert(GIMIsEnabled(pVM));
404 VMCPU_ASSERT_EMT(pVCpu);
405
406 switch (pVM->gim.s.enmProviderId)
407 {
408 case GIMPROVIDERID_HYPERV:
409 return gimHvReadMsr(pVCpu, idMsr, pRange, puValue);
410
411 case GIMPROVIDERID_KVM:
412 return gimKvmReadMsr(pVCpu, idMsr, pRange, puValue);
413
414 default:
415 AssertMsgFailed(("GIMReadMsr: for unknown provider %u idMsr=%#RX32 -> #GP(0)", pVM->gim.s.enmProviderId, idMsr));
416 return VERR_CPUM_RAISE_GP_0;
417 }
418}
419
420
421/**
422 * Invokes the write-MSR handler for the GIM provider configured for the VM.
423 *
424 * @returns Strict VBox status code like CPUMSetGuestMsr.
425 * @retval VINF_CPUM_R3_MSR_WRITE
426 * @retval VERR_CPUM_RAISE_GP_0
427 *
428 * @param pVCpu The cross context virtual CPU structure.
429 * @param idMsr The MSR to write.
430 * @param pRange The range this MSR belongs to.
431 * @param uValue The value to set, ignored bits masked.
432 * @param uRawValue The raw value with the ignored bits not masked.
433 */
434VMM_INT_DECL(VBOXSTRICTRC) GIMWriteMsr(PVMCPUCC pVCpu, uint32_t idMsr, PCCPUMMSRRANGE pRange, uint64_t uValue, uint64_t uRawValue)
435{
436 AssertPtr(pVCpu);
437 NOREF(uValue);
438
439 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
440 Assert(GIMIsEnabled(pVM));
441 VMCPU_ASSERT_EMT(pVCpu);
442
443 switch (pVM->gim.s.enmProviderId)
444 {
445 case GIMPROVIDERID_HYPERV:
446 return gimHvWriteMsr(pVCpu, idMsr, pRange, uRawValue);
447
448 case GIMPROVIDERID_KVM:
449 return gimKvmWriteMsr(pVCpu, idMsr, pRange, uRawValue);
450
451 default:
452 AssertMsgFailed(("GIMWriteMsr: for unknown provider %u idMsr=%#RX32 -> #GP(0)", pVM->gim.s.enmProviderId, idMsr));
453 return VERR_CPUM_RAISE_GP_0;
454 }
455}
456
457
458/**
459 * Queries the opcode bytes for a native hypercall.
460 *
461 * @returns VBox status code.
462 * @param pVM The cross context VM structure.
463 * @param pvBuf The destination buffer.
464 * @param cbBuf The size of the buffer.
465 * @param pcbWritten Where to return the number of bytes written. This is
466 * reliably updated only on successful return. Optional.
467 * @param puDisOpcode Where to return the disassembler opcode. Optional.
468 */
469VMM_INT_DECL(int) GIMQueryHypercallOpcodeBytes(PVM pVM, void *pvBuf, size_t cbBuf, size_t *pcbWritten, uint16_t *puDisOpcode)
470{
471 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
472
473#if defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64)
474 CPUMCPUVENDOR enmCpuVendor = CPUMGetHostCpuVendor(pVM);
475#else
476 CPUMCPUVENDOR enmCpuVendor = CPUMGetGuestCpuVendor(pVM); /* Use what is presented to the guest. */
477#endif
478 uint8_t const *pbSrc;
479 size_t cbSrc;
480 switch (enmCpuVendor)
481 {
482 case CPUMCPUVENDOR_AMD:
483 case CPUMCPUVENDOR_HYGON:
484 {
485 if (puDisOpcode)
486 *puDisOpcode = OP_VMMCALL;
487 static uint8_t const s_abHypercall[] = { 0x0F, 0x01, 0xD9 }; /* VMMCALL */
488 pbSrc = s_abHypercall;
489 cbSrc = sizeof(s_abHypercall);
490 break;
491 }
492
493 case CPUMCPUVENDOR_INTEL:
494 case CPUMCPUVENDOR_VIA:
495 case CPUMCPUVENDOR_SHANGHAI:
496 {
497 if (puDisOpcode)
498 *puDisOpcode = OP_VMCALL;
499 static uint8_t const s_abHypercall[] = { 0x0F, 0x01, 0xC1 }; /* VMCALL */
500 pbSrc = s_abHypercall;
501 cbSrc = sizeof(s_abHypercall);
502 break;
503 }
504
505 default:
506 AssertMsgFailedReturn(("%d\n", enmCpuVendor), VERR_UNSUPPORTED_CPU);
507 }
508 if (RT_LIKELY(cbBuf >= cbSrc))
509 {
510 memcpy(pvBuf, pbSrc, cbSrc);
511 if (pcbWritten)
512 *pcbWritten = cbSrc;
513 return VINF_SUCCESS;
514 }
515 return VERR_BUFFER_OVERFLOW;
516}
517#endif
518
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