VirtualBox

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

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

VMM/GIMAll: Use the guest CPU vendor non x86/amd64 hosts when selecting the vmcall/vmmcall instruction opcodes, bugref:9898

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