VirtualBox

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

Last change on this file since 96335 was 93554, checked in by vboxsync, 3 years ago

VMM: Changed PAGE_SIZE -> GUEST_PAGE_SIZE / HOST_PAGE_SIZE, PAGE_SHIFT -> GUEST_PAGE_SHIFT / HOST_PAGE_SHIFT, and PAGE_OFFSET_MASK -> GUEST_PAGE_OFFSET_MASK / HOST_PAGE_OFFSET_MASK. Also removed most usage of ASMMemIsZeroPage and ASMMemZeroPage since the host and guest page size doesn't need to be the same any more. Some work left to do in the page pool code. bugref:9898

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