VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/GIM.cpp@ 96407

Last change on this file since 96407 was 96407, checked in by vboxsync, 22 months ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 22.6 KB
Line 
1/* $Id: GIM.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * GIM - Guest Interface Manager.
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/** @page pg_gim GIM - The Guest Interface Manager
29 *
30 * The Guest Interface Manager abstracts an interface provider through which
31 * guests may interact with the hypervisor.
32 *
33 * @see grp_gim
34 *
35 *
36 * @section sec_gim_provider Providers
37 *
38 * A GIM provider implements a particular hypervisor interface such as Microsoft
39 * Hyper-V, Linux KVM and so on. It hooks into various components in the VMM to
40 * ease the guest in running under a recognized, virtualized environment.
41 *
42 * The GIM provider configured for the VM needs to be recognized by the guest OS
43 * in order to make use of features supported by the interface. Since it
44 * requires co-operation from the guest OS, a GIM provider may also be referred to
45 * as a paravirtualization interface.
46 *
47 * One of the goals of having a paravirtualized interface is for enabling guests
48 * to be more accurate and efficient when operating in a virtualized
49 * environment. For instance, a guest OS which interfaces to VirtualBox through
50 * a GIM provider may rely on the provider for supplying the correct TSC
51 * frequency of the host processor. The guest can then avoid caliberating the
52 * TSC itself, resulting in higher accuracy and better performance.
53 *
54 * At most, only one GIM provider can be active for a running VM and cannot be
55 * changed during the lifetime of the VM.
56 */
57
58
59/*********************************************************************************************************************************
60* Header Files *
61*********************************************************************************************************************************/
62#define LOG_GROUP LOG_GROUP_GIM
63#include <VBox/vmm/gim.h>
64#include <VBox/vmm/hm.h>
65#include <VBox/vmm/ssm.h>
66#include <VBox/vmm/pdmdev.h>
67#include "GIMInternal.h"
68#include <VBox/vmm/vm.h>
69
70#include <VBox/log.h>
71
72#include <iprt/err.h>
73#include <iprt/semaphore.h>
74#include <iprt/string.h>
75
76/* Include all GIM providers. */
77#include "GIMMinimalInternal.h"
78#include "GIMHvInternal.h"
79#include "GIMKvmInternal.h"
80
81
82/*********************************************************************************************************************************
83* Internal Functions *
84*********************************************************************************************************************************/
85static FNSSMINTSAVEEXEC gimR3Save;
86static FNSSMINTLOADEXEC gimR3Load;
87static FNSSMINTLOADDONE gimR3LoadDone;
88
89
90/**
91 * Initializes the GIM.
92 *
93 * @returns VBox status code.
94 * @param pVM The cross context VM structure.
95 */
96VMMR3_INT_DECL(int) GIMR3Init(PVM pVM)
97{
98 LogFlow(("GIMR3Init\n"));
99
100 /*
101 * Assert alignment and sizes.
102 */
103 AssertCompile(sizeof(pVM->gim.s) <= sizeof(pVM->gim.padding));
104 AssertCompile(sizeof(pVM->apCpusR3[0]->gim.s) <= sizeof(pVM->apCpusR3[0]->gim.padding));
105
106 /*
107 * Initialize members.
108 */
109 pVM->gim.s.hSemiReadOnlyMmio2Handler = NIL_PGMPHYSHANDLERTYPE;
110
111 /*
112 * Register the saved state data unit.
113 */
114 int rc = SSMR3RegisterInternal(pVM, "GIM", 0 /* uInstance */, GIM_SAVED_STATE_VERSION, sizeof(GIM),
115 NULL /* pfnLivePrep */, NULL /* pfnLiveExec */, NULL /* pfnLiveVote*/,
116 NULL /* pfnSavePrep */, gimR3Save, NULL /* pfnSaveDone */,
117 NULL /* pfnLoadPrep */, gimR3Load, gimR3LoadDone);
118 if (RT_FAILURE(rc))
119 return rc;
120
121 /*
122 * Read configuration.
123 */
124 PCFGMNODE pCfgNode = CFGMR3GetChild(CFGMR3GetRoot(pVM), "GIM/");
125
126 /*
127 * Validate the GIM settings.
128 */
129 rc = CFGMR3ValidateConfig(pCfgNode, "/GIM/", /* pszNode */
130 "Provider" /* pszValidValues */
131 "|Version",
132 "HyperV", /* pszValidNodes */
133 "GIM", /* pszWho */
134 0); /* uInstance */
135 if (RT_FAILURE(rc))
136 return rc;
137
138 /** @cfgm{/GIM/Provider, string}
139 * The name of the GIM provider. The default is "none". */
140 char szProvider[64];
141 rc = CFGMR3QueryStringDef(pCfgNode, "Provider", szProvider, sizeof(szProvider), "None");
142 AssertLogRelRCReturn(rc, rc);
143
144 /** @cfgm{/GIM/Version, uint32_t}
145 * The interface version. The default is 0, which means "provide the most
146 * up-to-date implementation". */
147 uint32_t uVersion;
148 rc = CFGMR3QueryU32Def(pCfgNode, "Version", &uVersion, 0 /* default */);
149 AssertLogRelRCReturn(rc, rc);
150
151 /*
152 * Setup the GIM provider for this VM.
153 */
154 LogRel(("GIM: Using provider '%s' (Implementation version: %u)\n", szProvider, uVersion));
155 if (!RTStrCmp(szProvider, "None"))
156 pVM->gim.s.enmProviderId = GIMPROVIDERID_NONE;
157 else
158 {
159 pVM->gim.s.u32Version = uVersion;
160 /** @todo r=bird: Because u32Version is saved, it should be translated to the
161 * 'most up-to-date implementation' version number when 0. Otherwise,
162 * we'll have abiguities when loading the state of older VMs. */
163 if (!RTStrCmp(szProvider, "Minimal"))
164 {
165 pVM->gim.s.enmProviderId = GIMPROVIDERID_MINIMAL;
166 rc = gimR3MinimalInit(pVM);
167 }
168 else if (!RTStrCmp(szProvider, "HyperV"))
169 {
170 pVM->gim.s.enmProviderId = GIMPROVIDERID_HYPERV;
171 rc = gimR3HvInit(pVM, pCfgNode);
172 }
173 else if (!RTStrCmp(szProvider, "KVM"))
174 {
175 pVM->gim.s.enmProviderId = GIMPROVIDERID_KVM;
176 rc = gimR3KvmInit(pVM);
177 }
178 else
179 rc = VMR3SetError(pVM->pUVM, VERR_GIM_INVALID_PROVIDER, RT_SRC_POS, "Provider '%s' unknown.", szProvider);
180 }
181
182 /*
183 * Statistics.
184 */
185 STAM_REL_REG_USED(pVM, &pVM->gim.s.StatDbgXmit, STAMTYPE_COUNTER, "/GIM/Debug/Transmit", STAMUNIT_OCCURENCES, "Debug packets sent.");
186 STAM_REL_REG_USED(pVM, &pVM->gim.s.StatDbgXmitBytes, STAMTYPE_COUNTER, "/GIM/Debug/TransmitBytes", STAMUNIT_OCCURENCES, "Debug bytes sent.");
187 STAM_REL_REG_USED(pVM, &pVM->gim.s.StatDbgRecv, STAMTYPE_COUNTER, "/GIM/Debug/Receive", STAMUNIT_OCCURENCES, "Debug packets received.");
188 STAM_REL_REG_USED(pVM, &pVM->gim.s.StatDbgRecvBytes, STAMTYPE_COUNTER, "/GIM/Debug/ReceiveBytes", STAMUNIT_OCCURENCES, "Debug bytes received.");
189
190 STAM_REL_REG_USED(pVM, &pVM->gim.s.StatHypercalls, STAMTYPE_COUNTER, "/GIM/Hypercalls", STAMUNIT_OCCURENCES, "Number of hypercalls initiated.");
191 return rc;
192}
193
194
195/**
196 * Initializes the remaining bits of the GIM provider.
197 *
198 * This is called after initializing HM and most other VMM components.
199 *
200 * @returns VBox status code.
201 * @param pVM The cross context VM structure.
202 * @thread EMT(0)
203 */
204VMMR3_INT_DECL(int) GIMR3InitCompleted(PVM pVM)
205{
206 switch (pVM->gim.s.enmProviderId)
207 {
208 case GIMPROVIDERID_MINIMAL:
209 return gimR3MinimalInitCompleted(pVM);
210
211 case GIMPROVIDERID_HYPERV:
212 return gimR3HvInitCompleted(pVM);
213
214 case GIMPROVIDERID_KVM:
215 return gimR3KvmInitCompleted(pVM);
216
217 default:
218 break;
219 }
220
221 if (!TMR3CpuTickIsFixedRateMonotonic(pVM, true /* fWithParavirtEnabled */))
222 LogRel(("GIM: Warning!!! Host TSC is unstable. The guest may behave unpredictably with a paravirtualized clock.\n"));
223
224 return VINF_SUCCESS;
225}
226
227
228/**
229 * @callback_method_impl{FNSSMINTSAVEEXEC}
230 */
231static DECLCALLBACK(int) gimR3Save(PVM pVM, PSSMHANDLE pSSM)
232{
233 AssertReturn(pVM, VERR_INVALID_PARAMETER);
234 AssertReturn(pSSM, VERR_SSM_INVALID_STATE);
235
236 int rc = VINF_SUCCESS;
237#if 0
238 /* Save per-CPU data. */
239 SSMR3PutU32(pSSM, pVM->cCpus);
240 for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
241 {
242 PVMCPU pVCpu = pVM->apCpusR3[idCpu];
243 rc = SSMR3PutXYZ(pSSM, pVCpu->gim.s.XYZ);
244 }
245#endif
246
247 /*
248 * Save per-VM data.
249 */
250 SSMR3PutU32(pSSM, pVM->gim.s.enmProviderId);
251 SSMR3PutU32(pSSM, pVM->gim.s.u32Version);
252
253 /*
254 * Save provider-specific data.
255 */
256 switch (pVM->gim.s.enmProviderId)
257 {
258 case GIMPROVIDERID_HYPERV:
259 rc = gimR3HvSave(pVM, pSSM);
260 AssertRCReturn(rc, rc);
261 break;
262
263 case GIMPROVIDERID_KVM:
264 rc = gimR3KvmSave(pVM, pSSM);
265 AssertRCReturn(rc, rc);
266 break;
267
268 default:
269 break;
270 }
271
272 return rc;
273}
274
275
276/**
277 * @callback_method_impl{FNSSMINTLOADEXEC}
278 */
279static DECLCALLBACK(int) gimR3Load(PVM pVM, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
280{
281 if (uPass != SSM_PASS_FINAL)
282 return VINF_SUCCESS;
283 if (uVersion != GIM_SAVED_STATE_VERSION)
284 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
285
286 int rc;
287#if 0
288 /* Load per-CPU data. */
289 for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
290 {
291 PVMCPU pVCpu = pVM->apCpusR3[idCpu];
292 rc = SSMR3PutXYZ(pSSM, pVCpu->gim.s.XYZ);
293 }
294#endif
295
296 /*
297 * Load per-VM data.
298 */
299 uint32_t uProviderId;
300 uint32_t uProviderVersion;
301
302 SSMR3GetU32(pSSM, &uProviderId);
303 rc = SSMR3GetU32(pSSM, &uProviderVersion);
304 AssertRCReturn(rc, rc);
305
306 if ((GIMPROVIDERID)uProviderId != pVM->gim.s.enmProviderId)
307 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Saved GIM provider %u differs from the configured one (%u)."),
308 uProviderId, pVM->gim.s.enmProviderId);
309#if 0 /** @todo r=bird: Figure out what you mean to do here with the version. */
310 if (uProviderVersion != pVM->gim.s.u32Version)
311 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Saved GIM provider version %u differs from the configured one (%u)."),
312 uProviderVersion, pVM->gim.s.u32Version);
313#else
314 pVM->gim.s.u32Version = uProviderVersion;
315#endif
316
317 /*
318 * Load provider-specific data.
319 */
320 switch (pVM->gim.s.enmProviderId)
321 {
322 case GIMPROVIDERID_HYPERV:
323 rc = gimR3HvLoad(pVM, pSSM);
324 AssertRCReturn(rc, rc);
325 break;
326
327 case GIMPROVIDERID_KVM:
328 rc = gimR3KvmLoad(pVM, pSSM);
329 AssertRCReturn(rc, rc);
330 break;
331
332 default:
333 break;
334 }
335
336 return VINF_SUCCESS;
337}
338
339
340/**
341 * @callback_method_impl{FNSSMINTLOADDONE}
342 */
343static DECLCALLBACK(int) gimR3LoadDone(PVM pVM, PSSMHANDLE pSSM)
344{
345 switch (pVM->gim.s.enmProviderId)
346 {
347 case GIMPROVIDERID_HYPERV:
348 return gimR3HvLoadDone(pVM, pSSM);
349
350 default:
351 return VINF_SUCCESS;
352 }
353}
354
355
356/**
357 * Terminates the GIM.
358 *
359 * Termination means cleaning up and freeing all resources,
360 * the VM itself is, at this point, powered off or suspended.
361 *
362 * @returns VBox status code.
363 * @param pVM The cross context VM structure.
364 */
365VMMR3_INT_DECL(int) GIMR3Term(PVM pVM)
366{
367 switch (pVM->gim.s.enmProviderId)
368 {
369 case GIMPROVIDERID_HYPERV:
370 return gimR3HvTerm(pVM);
371
372 case GIMPROVIDERID_KVM:
373 return gimR3KvmTerm(pVM);
374
375 default:
376 break;
377 }
378 return VINF_SUCCESS;
379}
380
381
382/**
383 * Applies relocations to data and code managed by this
384 * component. This function will be called at init and
385 * whenever the VMM need to relocate it self inside the GC.
386 *
387 * @param pVM The cross context VM structure.
388 * @param offDelta Relocation delta relative to old location.
389 */
390VMMR3_INT_DECL(void) GIMR3Relocate(PVM pVM, RTGCINTPTR offDelta)
391{
392 switch (pVM->gim.s.enmProviderId)
393 {
394 case GIMPROVIDERID_HYPERV:
395 gimR3HvRelocate(pVM, offDelta);
396 break;
397
398 default:
399 break;
400 }
401}
402
403
404/**
405 * The VM is being reset.
406 *
407 * For the GIM component this means unmapping and unregistering MMIO2 regions
408 * and other provider-specific resets.
409 *
410 * @returns VBox status code.
411 * @param pVM The cross context VM structure.
412 */
413VMMR3_INT_DECL(void) GIMR3Reset(PVM pVM)
414{
415 switch (pVM->gim.s.enmProviderId)
416 {
417 case GIMPROVIDERID_HYPERV:
418 return gimR3HvReset(pVM);
419
420 case GIMPROVIDERID_KVM:
421 return gimR3KvmReset(pVM);
422
423 default:
424 break;
425 }
426}
427
428
429/**
430 * Registers the GIM device with VMM.
431 *
432 * @param pVM The cross context VM structure.
433 * @param pDevIns Pointer to the GIM device instance.
434 * @param pDbg Pointer to the GIM device debug structure, can be
435 * NULL.
436 */
437VMMR3DECL(void) GIMR3GimDeviceRegister(PVM pVM, PPDMDEVINS pDevIns, PGIMDEBUG pDbg)
438{
439 pVM->gim.s.pDevInsR3 = pDevIns;
440 pVM->gim.s.pDbgR3 = pDbg;
441}
442
443
444/**
445 * Gets debug setup specified by the provider.
446 *
447 * @returns VBox status code.
448 * @param pVM The cross context VM structure.
449 * @param pDbgSetup Where to store the debug setup details.
450 */
451VMMR3DECL(int) GIMR3GetDebugSetup(PVM pVM, PGIMDEBUGSETUP pDbgSetup)
452{
453 AssertReturn(pVM, VERR_INVALID_PARAMETER);
454 AssertReturn(pDbgSetup, VERR_INVALID_PARAMETER);
455
456 switch (pVM->gim.s.enmProviderId)
457 {
458 case GIMPROVIDERID_HYPERV:
459 return gimR3HvGetDebugSetup(pVM, pDbgSetup);
460 default:
461 break;
462 }
463 return VERR_GIM_NO_DEBUG_CONNECTION;
464}
465
466
467/**
468 * Read data from a host debug session.
469 *
470 * @returns VBox status code.
471 *
472 * @param pVM The cross context VM structure.
473 * @param pvRead The read buffer.
474 * @param pcbRead The size of the read buffer as well as where to store
475 * the number of bytes read.
476 * @param pfnReadComplete Callback when the buffer has been read and
477 * before signalling reading of the next buffer.
478 * Optional, can be NULL.
479 * @thread EMT.
480 */
481VMMR3_INT_DECL(int) gimR3DebugRead(PVM pVM, void *pvRead, size_t *pcbRead, PFNGIMDEBUGBUFREADCOMPLETED pfnReadComplete)
482{
483 PGIMDEBUG pDbg = pVM->gim.s.pDbgR3;
484 if (pDbg)
485 {
486 if (ASMAtomicReadBool(&pDbg->fDbgRecvBufRead) == true)
487 {
488 STAM_REL_COUNTER_INC(&pVM->gim.s.StatDbgRecv);
489 STAM_REL_COUNTER_ADD(&pVM->gim.s.StatDbgRecvBytes, pDbg->cbDbgRecvBufRead);
490
491 memcpy(pvRead, pDbg->pvDbgRecvBuf, pDbg->cbDbgRecvBufRead);
492 *pcbRead = pDbg->cbDbgRecvBufRead;
493 if (pfnReadComplete)
494 pfnReadComplete(pVM);
495 RTSemEventMultiSignal(pDbg->hDbgRecvThreadSem);
496 ASMAtomicWriteBool(&pDbg->fDbgRecvBufRead, false);
497 return VINF_SUCCESS;
498 }
499 else
500 *pcbRead = 0;
501 return VERR_NO_DATA;
502 }
503 return VERR_GIM_NO_DEBUG_CONNECTION;
504}
505
506
507/**
508 * Write data to a host debug session.
509 *
510 * @returns VBox status code.
511 *
512 * @param pVM The cross context VM structure.
513 * @param pvWrite The write buffer.
514 * @param pcbWrite The size of the write buffer as well as where to store
515 * the number of bytes written.
516 * @thread EMT.
517 */
518VMMR3_INT_DECL(int) gimR3DebugWrite(PVM pVM, void *pvWrite, size_t *pcbWrite)
519{
520 PGIMDEBUG pDbg = pVM->gim.s.pDbgR3;
521 if (pDbg)
522 {
523 PPDMISTREAM pDbgStream = pDbg->pDbgDrvStream;
524 if (pDbgStream)
525 {
526 size_t cbWrite = *pcbWrite;
527 int rc = pDbgStream->pfnWrite(pDbgStream, pvWrite, pcbWrite);
528 if ( RT_SUCCESS(rc)
529 && *pcbWrite == cbWrite)
530 {
531 STAM_REL_COUNTER_INC(&pVM->gim.s.StatDbgXmit);
532 STAM_REL_COUNTER_ADD(&pVM->gim.s.StatDbgXmitBytes, *pcbWrite);
533 }
534 return rc;
535 }
536 }
537 return VERR_GIM_NO_DEBUG_CONNECTION;
538}
539
540#if 0 /* ??? */
541
542/**
543 * @callback_method_impl{FNPGMPHYSHANDLER,
544 * Write access handler for mapped MMIO2 pages. Currently ignores writes.}
545 *
546 * @todo In the future we might want to let the GIM provider decide what the
547 * handler should do (like throwing \#GP faults).
548 */
549static DECLCALLBACK(VBOXSTRICTRC) gimR3Mmio2WriteHandler(PVM pVM, PVMCPU pVCpu, RTGCPHYS GCPhys, void *pvPhys, void *pvBuf,
550 size_t cbBuf, PGMACCESSTYPE enmAccessType, PGMACCESSORIGIN enmOrigin,
551 void *pvUser)
552{
553 RT_NOREF6(pVM, pVCpu, GCPhys, pvPhys, pvBuf, cbBuf);
554 RT_NOREF3(enmAccessType, enmOrigin, pvUser);
555
556 /*
557 * Ignore writes to the mapped MMIO2 page.
558 */
559 Assert(enmAccessType == PGMACCESSTYPE_WRITE);
560 return VINF_SUCCESS; /** @todo Hyper-V says we should \#GP(0) fault for writes to the Hypercall and TSC page. */
561}
562
563
564/**
565 * Unmaps a registered MMIO2 region in the guest address space and removes any
566 * access handlers for it.
567 *
568 * @returns VBox status code.
569 * @param pVM The cross context VM structure.
570 * @param pRegion Pointer to the GIM MMIO2 region.
571 */
572VMMR3_INT_DECL(int) gimR3Mmio2Unmap(PVM pVM, PGIMMMIO2REGION pRegion)
573{
574 AssertPtr(pVM);
575 AssertPtr(pRegion);
576
577 PPDMDEVINS pDevIns = pVM->gim.s.pDevInsR3;
578 AssertPtr(pDevIns);
579 if (pRegion->fMapped)
580 {
581 int rc = PGMHandlerPhysicalDeregister(pVM, pRegion->GCPhysPage);
582 AssertRC(rc);
583
584 rc = PDMDevHlpMMIO2Unmap(pDevIns, pRegion->iRegion, pRegion->GCPhysPage);
585 if (RT_SUCCESS(rc))
586 {
587 pRegion->fMapped = false;
588 pRegion->GCPhysPage = NIL_RTGCPHYS;
589 }
590 }
591 return VINF_SUCCESS;
592}
593
594
595/**
596 * Maps a registered MMIO2 region in the guest address space.
597 *
598 * The region will be made read-only and writes from the guest will be ignored.
599 *
600 * @returns VBox status code.
601 * @param pVM The cross context VM structure.
602 * @param pRegion Pointer to the GIM MMIO2 region.
603 * @param GCPhysRegion Where in the guest address space to map the region.
604 */
605VMMR3_INT_DECL(int) GIMR3Mmio2Map(PVM pVM, PGIMMMIO2REGION pRegion, RTGCPHYS GCPhysRegion)
606{
607 PPDMDEVINS pDevIns = pVM->gim.s.pDevInsR3;
608 AssertPtr(pDevIns);
609
610 /* The guest-physical address must be page-aligned. */
611 if (GCPhysRegion & GUEST_PAGE_OFFSET_MASK)
612 {
613 LogFunc(("%s: %#RGp not paging aligned\n", pRegion->szDescription, GCPhysRegion));
614 return VERR_PGM_INVALID_GC_PHYSICAL_ADDRESS;
615 }
616
617 /* Allow only normal pages to be overlaid using our MMIO2 pages (disallow MMIO, ROM, reserved pages). */
618 /** @todo Hyper-V doesn't seem to be very strict about this, may be relax
619 * later if some guest really requires it. */
620 if (!PGMPhysIsGCPhysNormal(pVM, GCPhysRegion))
621 {
622 LogFunc(("%s: %#RGp is not normal memory\n", pRegion->szDescription, GCPhysRegion));
623 return VERR_PGM_INVALID_GC_PHYSICAL_ADDRESS;
624 }
625
626 if (!pRegion->fRegistered)
627 {
628 LogFunc(("%s: Region has not been registered.\n", pRegion->szDescription));
629 return VERR_GIM_IPE_1;
630 }
631
632 /*
633 * Map the MMIO2 region over the specified guest-physical address.
634 */
635 int rc = PDMDevHlpMMIOExMap(pDevIns, NULL, pRegion->iRegion, GCPhysRegion);
636 if (RT_SUCCESS(rc))
637 {
638 /*
639 * Install access-handlers for the mapped page to prevent (ignore) writes to it
640 * from the guest.
641 */
642 if (pVM->gim.s.hSemiReadOnlyMmio2Handler == NIL_PGMPHYSHANDLERTYPE)
643 rc = PGMR3HandlerPhysicalTypeRegister(pVM, PGMPHYSHANDLERKIND_WRITE,
644 gimR3Mmio2WriteHandler,
645 NULL /* pszModR0 */, NULL /* pszHandlerR0 */, NULL /* pszPfHandlerR0 */,
646 NULL /* pszModRC */, NULL /* pszHandlerRC */, NULL /* pszPfHandlerRC */,
647 "GIM read-only MMIO2 handler",
648 &pVM->gim.s.hSemiReadOnlyMmio2Handler);
649 if (RT_SUCCESS(rc))
650 {
651 rc = PGMHandlerPhysicalRegister(pVM, GCPhysRegion, GCPhysRegion + (pRegion->cbRegion - 1),
652 pVM->gim.s.hSemiReadOnlyMmio2Handler,
653 NULL /* pvUserR3 */, NIL_RTR0PTR /* pvUserR0 */, NIL_RTRCPTR /* pvUserRC */,
654 pRegion->szDescription);
655 if (RT_SUCCESS(rc))
656 {
657 pRegion->fMapped = true;
658 pRegion->GCPhysPage = GCPhysRegion;
659 return rc;
660 }
661 }
662
663 PDMDevHlpMMIO2Unmap(pDevIns, pRegion->iRegion, GCPhysRegion);
664 }
665
666 return rc;
667}
668
669
670/**
671 * Registers the physical handler for the registered and mapped MMIO2 region.
672 *
673 * @returns VBox status code.
674 * @param pVM The cross context VM structure.
675 * @param pRegion Pointer to the GIM MMIO2 region.
676 */
677VMMR3_INT_DECL(int) gimR3Mmio2HandlerPhysicalRegister(PVM pVM, PGIMMMIO2REGION pRegion)
678{
679 AssertPtr(pRegion);
680 AssertReturn(pRegion->fRegistered, VERR_GIM_IPE_2);
681 AssertReturn(pRegion->fMapped, VERR_GIM_IPE_3);
682
683 return PGMR3HandlerPhysicalRegister(pVM,
684 PGMPHYSHANDLERKIND_WRITE,
685 pRegion->GCPhysPage, pRegion->GCPhysPage + (pRegion->cbRegion - 1),
686 gimR3Mmio2WriteHandler, NULL /* pvUserR3 */,
687 NULL /* pszModR0 */, NULL /* pszHandlerR0 */, NIL_RTR0PTR /* pvUserR0 */,
688 NULL /* pszModRC */, NULL /* pszHandlerRC */, NIL_RTRCPTR /* pvUserRC */,
689 pRegion->szDescription);
690}
691
692
693/**
694 * Deregisters the physical handler for the MMIO2 region.
695 *
696 * @returns VBox status code.
697 * @param pVM The cross context VM structure.
698 * @param pRegion Pointer to the GIM MMIO2 region.
699 */
700VMMR3_INT_DECL(int) gimR3Mmio2HandlerPhysicalDeregister(PVM pVM, PGIMMMIO2REGION pRegion)
701{
702 return PGMHandlerPhysicalDeregister(pVM, pRegion->GCPhysPage);
703}
704
705#endif
706
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use