VirtualBox

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

Last change on this file since 77807 was 76886, checked in by vboxsync, 6 years ago

VMM (and related changes): Add support for Shanghai/Zhaoxin CPUs. Modified and improved contribution by Journey Ren submitted under MIT license. Thank you!

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