VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR0/IOMR0Mmio.cpp

Last change on this file was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.6 KB
Line 
1/* $Id: IOMR0Mmio.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * IOM - Host Context Ring 0, MMIO.
4 */
5
6/*
7 * Copyright (C) 2006-2024 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_IOM_MMIO
33#include <VBox/vmm/iom.h>
34#include "IOMInternal.h"
35#include <VBox/vmm/pdmdev.h>
36#include <VBox/vmm/vmcc.h>
37#include <VBox/err.h>
38#include <VBox/log.h>
39#include <iprt/assert.h>
40#include <iprt/mem.h>
41#include <iprt/memobj.h>
42#include <iprt/process.h>
43#include <iprt/string.h>
44
45
46
47/**
48 * Initializes the MMIO related members.
49 *
50 * @param pGVM Pointer to the global VM structure.
51 */
52void iomR0MmioInitPerVMData(PGVM pGVM)
53{
54 pGVM->iomr0.s.hMmioMapObj = NIL_RTR0MEMOBJ;
55 pGVM->iomr0.s.hMmioMemObj = NIL_RTR0MEMOBJ;
56#ifdef VBOX_WITH_STATISTICS
57 pGVM->iomr0.s.hMmioStatsMapObj = NIL_RTR0MEMOBJ;
58 pGVM->iomr0.s.hMmioStatsMemObj = NIL_RTR0MEMOBJ;
59#endif
60}
61
62
63/**
64 * Cleans up MMIO related resources.
65 */
66void iomR0MmioCleanupVM(PGVM pGVM)
67{
68 RTR0MemObjFree(pGVM->iomr0.s.hMmioMapObj, true /*fFreeMappings*/);
69 pGVM->iomr0.s.hMmioMapObj = NIL_RTR0MEMOBJ;
70 RTR0MemObjFree(pGVM->iomr0.s.hMmioMemObj, true /*fFreeMappings*/);
71 pGVM->iomr0.s.hMmioMemObj = NIL_RTR0MEMOBJ;
72#ifdef VBOX_WITH_STATISTICS
73 RTR0MemObjFree(pGVM->iomr0.s.hMmioStatsMapObj, true /*fFreeMappings*/);
74 pGVM->iomr0.s.hMmioStatsMapObj = NIL_RTR0MEMOBJ;
75 RTR0MemObjFree(pGVM->iomr0.s.hMmioStatsMemObj, true /*fFreeMappings*/);
76 pGVM->iomr0.s.hMmioStatsMemObj = NIL_RTR0MEMOBJ;
77#endif
78}
79
80
81/**
82 * Implements PDMDEVHLPR0::pfnMmioSetUpContext.
83 *
84 * @param pGVM The global (ring-0) VM structure.
85 * @param pDevIns The device instance.
86 * @param hRegion The MMIO region handle (already registered in
87 * ring-3).
88 * @param pfnWrite The write handler callback, optional.
89 * @param pfnRead The read handler callback, optional.
90 * @param pfnFill The fill handler callback, optional.
91 * @param pvUser User argument for the callbacks.
92 * @thread EMT(0)
93 * @note Only callable at VM creation time.
94 */
95VMMR0_INT_DECL(int) IOMR0MmioSetUpContext(PGVM pGVM, PPDMDEVINS pDevIns, IOMMMIOHANDLE hRegion, PFNIOMMMIONEWWRITE pfnWrite,
96 PFNIOMMMIONEWREAD pfnRead, PFNIOMMMIONEWFILL pfnFill, void *pvUser)
97{
98 /*
99 * Validate input and state.
100 */
101 VM_ASSERT_EMT0_RETURN(pGVM, VERR_VM_THREAD_NOT_EMT);
102 VM_ASSERT_STATE_RETURN(pGVM, VMSTATE_CREATING, VERR_VM_INVALID_VM_STATE);
103 AssertReturn(hRegion < pGVM->iomr0.s.cMmioAlloc, VERR_IOM_INVALID_MMIO_HANDLE);
104 AssertReturn(hRegion < pGVM->iom.s.cMmioRegs, VERR_IOM_INVALID_MMIO_HANDLE);
105 AssertPtrReturn(pDevIns, VERR_INVALID_HANDLE);
106 AssertReturn(pDevIns->pDevInsForR3 != NIL_RTR3PTR && !(pDevIns->pDevInsForR3 & HOST_PAGE_OFFSET_MASK), VERR_INVALID_PARAMETER);
107 AssertReturn(pGVM->iomr0.s.paMmioRing3Regs[hRegion].pDevIns == pDevIns->pDevInsForR3, VERR_IOM_INVALID_MMIO_HANDLE);
108 AssertReturn(pGVM->iomr0.s.paMmioRegs[hRegion].pDevIns == NULL, VERR_WRONG_ORDER);
109 Assert(pGVM->iomr0.s.paMmioRegs[hRegion].idxSelf == hRegion);
110
111 AssertReturn(pfnWrite || pfnRead || pfnFill, VERR_INVALID_PARAMETER);
112 AssertPtrNullReturn(pfnWrite, VERR_INVALID_POINTER);
113 AssertPtrNullReturn(pfnRead, VERR_INVALID_POINTER);
114 AssertPtrNullReturn(pfnFill, VERR_INVALID_POINTER);
115
116 uint32_t const fFlags = pGVM->iomr0.s.paMmioRing3Regs[hRegion].fFlags;
117 RTGCPHYS const cbRegion = pGVM->iomr0.s.paMmioRing3Regs[hRegion].cbRegion;
118 AssertMsgReturn(cbRegion > 0 && cbRegion <= _1T, ("cbRegion=%#RGp\n", cbRegion), VERR_IOM_INVALID_MMIO_HANDLE);
119
120 /*
121 * Do the job.
122 */
123 pGVM->iomr0.s.paMmioRegs[hRegion].cbRegion = cbRegion;
124 pGVM->iomr0.s.paMmioRegs[hRegion].pvUser = pvUser;
125 pGVM->iomr0.s.paMmioRegs[hRegion].pDevIns = pDevIns;
126 pGVM->iomr0.s.paMmioRegs[hRegion].pfnWriteCallback = pfnWrite;
127 pGVM->iomr0.s.paMmioRegs[hRegion].pfnReadCallback = pfnRead;
128 pGVM->iomr0.s.paMmioRegs[hRegion].pfnFillCallback = pfnFill;
129 pGVM->iomr0.s.paMmioRegs[hRegion].fFlags = fFlags;
130#ifdef VBOX_WITH_STATISTICS
131 uint16_t const idxStats = pGVM->iomr0.s.paMmioRing3Regs[hRegion].idxStats;
132 pGVM->iomr0.s.paMmioRegs[hRegion].idxStats = (uint32_t)idxStats < pGVM->iomr0.s.cMmioStatsAllocation
133 ? idxStats : UINT16_MAX;
134#else
135 pGVM->iomr0.s.paMmioRegs[hRegion].idxStats = UINT16_MAX;
136#endif
137
138 pGVM->iomr0.s.paMmioRing3Regs[hRegion].fRing0 = true;
139
140 return VINF_SUCCESS;
141}
142
143
144/**
145 * Grows the MMIO registration (all contexts) and lookup tables.
146 *
147 * @returns VBox status code.
148 * @param pGVM The global (ring-0) VM structure.
149 * @param cReqMinEntries The minimum growth (absolute).
150 * @thread EMT(0)
151 * @note Only callable at VM creation time.
152 */
153VMMR0_INT_DECL(int) IOMR0MmioGrowRegistrationTables(PGVM pGVM, uint64_t cReqMinEntries)
154{
155 /*
156 * Validate input and state.
157 */
158 VM_ASSERT_EMT0_RETURN(pGVM, VERR_VM_THREAD_NOT_EMT);
159 VM_ASSERT_STATE_RETURN(pGVM, VMSTATE_CREATING, VERR_VM_INVALID_VM_STATE);
160 AssertReturn(cReqMinEntries <= _4K, VERR_IOM_TOO_MANY_MMIO_REGISTRATIONS);
161 uint32_t cNewEntries = (uint32_t)cReqMinEntries;
162 AssertReturn(cNewEntries >= pGVM->iom.s.cMmioAlloc, VERR_IOM_MMIO_IPE_1);
163 uint32_t const cOldEntries = pGVM->iomr0.s.cMmioAlloc;
164 ASMCompilerBarrier();
165 AssertReturn(cNewEntries >= cOldEntries, VERR_IOM_MMIO_IPE_2);
166 AssertReturn(pGVM->iom.s.cMmioRegs >= pGVM->iomr0.s.cMmioMax, VERR_IOM_MMIO_IPE_3);
167
168 /*
169 * Allocate the new tables. We use a single allocation for the three tables (ring-0,
170 * ring-3, lookup) and does a partial mapping of the result to ring-3.
171 */
172 uint32_t const cbRing0 = RT_ALIGN_32(cNewEntries * sizeof(IOMMMIOENTRYR0), HOST_PAGE_SIZE);
173 uint32_t const cbRing3 = RT_ALIGN_32(cNewEntries * sizeof(IOMMMIOENTRYR3), HOST_PAGE_SIZE);
174 uint32_t const cbShared = RT_ALIGN_32(cNewEntries * sizeof(IOMMMIOLOOKUPENTRY), HOST_PAGE_SIZE);
175 uint32_t const cbNew = cbRing0 + cbRing3 + cbShared;
176
177 /* Use the rounded up space as best we can. */
178 cNewEntries = RT_MIN(RT_MIN(cbRing0 / sizeof(IOMMMIOENTRYR0), cbRing3 / sizeof(IOMMMIOENTRYR3)),
179 cbShared / sizeof(IOMMMIOLOOKUPENTRY));
180
181 RTR0MEMOBJ hMemObj;
182 int rc = RTR0MemObjAllocPage(&hMemObj, cbNew, false /*fExecutable*/);
183 if (RT_SUCCESS(rc))
184 {
185 /*
186 * Zero and map it.
187 */
188 RT_BZERO(RTR0MemObjAddress(hMemObj), cbNew);
189
190 RTR0MEMOBJ hMapObj;
191 rc = RTR0MemObjMapUserEx(&hMapObj, hMemObj, (RTR3PTR)-1, HOST_PAGE_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE,
192 RTR0ProcHandleSelf(), cbRing0, cbNew - cbRing0);
193 if (RT_SUCCESS(rc))
194 {
195 PIOMMMIOENTRYR0 const paRing0 = (PIOMMMIOENTRYR0)RTR0MemObjAddress(hMemObj);
196 PIOMMMIOENTRYR3 const paRing3 = (PIOMMMIOENTRYR3)((uintptr_t)paRing0 + cbRing0);
197 PIOMMMIOLOOKUPENTRY const paLookup = (PIOMMMIOLOOKUPENTRY)((uintptr_t)paRing3 + cbRing3);
198 RTR3UINTPTR const uAddrRing3 = RTR0MemObjAddressR3(hMapObj);
199
200 /*
201 * Copy over the old info and initialize the idxSelf and idxStats members.
202 */
203 if (pGVM->iomr0.s.paMmioRegs != NULL)
204 {
205 memcpy(paRing0, pGVM->iomr0.s.paMmioRegs, sizeof(paRing0[0]) * cOldEntries);
206 memcpy(paRing3, pGVM->iomr0.s.paMmioRing3Regs, sizeof(paRing3[0]) * cOldEntries);
207 memcpy(paLookup, pGVM->iomr0.s.paMmioLookup, sizeof(paLookup[0]) * cOldEntries);
208 }
209
210 size_t i = cbRing0 / sizeof(*paRing0);
211 while (i-- > cOldEntries)
212 {
213 paRing0[i].idxSelf = (uint16_t)i;
214 paRing0[i].idxStats = UINT16_MAX;
215 }
216 i = cbRing3 / sizeof(*paRing3);
217 while (i-- > cOldEntries)
218 {
219 paRing3[i].idxSelf = (uint16_t)i;
220 paRing3[i].idxStats = UINT16_MAX;
221 }
222
223 /*
224 * Switch the memory handles.
225 */
226 RTR0MEMOBJ hTmp = pGVM->iomr0.s.hMmioMapObj;
227 pGVM->iomr0.s.hMmioMapObj = hMapObj;
228 hMapObj = hTmp;
229
230 hTmp = pGVM->iomr0.s.hMmioMemObj;
231 pGVM->iomr0.s.hMmioMemObj = hMemObj;
232 hMemObj = hTmp;
233
234 /*
235 * Update the variables.
236 */
237 pGVM->iomr0.s.paMmioRegs = paRing0;
238 pGVM->iomr0.s.paMmioRing3Regs = paRing3;
239 pGVM->iomr0.s.paMmioLookup = paLookup;
240 pGVM->iom.s.paMmioRegs = uAddrRing3;
241 pGVM->iom.s.paMmioLookup = uAddrRing3 + cbRing3;
242 pGVM->iom.s.cMmioAlloc = cNewEntries;
243 pGVM->iomr0.s.cMmioAlloc = cNewEntries;
244
245 /*
246 * Free the old allocation.
247 */
248 RTR0MemObjFree(hMapObj, true /*fFreeMappings*/);
249 }
250 RTR0MemObjFree(hMemObj, true /*fFreeMappings*/);
251 }
252
253 return rc;
254}
255
256
257/**
258 * Grows the MMIO statistics table.
259 *
260 * @returns VBox status code.
261 * @param pGVM The global (ring-0) VM structure.
262 * @param cReqMinEntries The minimum growth (absolute).
263 * @thread EMT(0)
264 * @note Only callable at VM creation time.
265 */
266VMMR0_INT_DECL(int) IOMR0MmioGrowStatisticsTable(PGVM pGVM, uint64_t cReqMinEntries)
267{
268 /*
269 * Validate input and state.
270 */
271 VM_ASSERT_EMT0_RETURN(pGVM, VERR_VM_THREAD_NOT_EMT);
272 VM_ASSERT_STATE_RETURN(pGVM, VMSTATE_CREATING, VERR_VM_INVALID_VM_STATE);
273 AssertReturn(cReqMinEntries <= _64K, VERR_IOM_TOO_MANY_MMIO_REGISTRATIONS);
274 uint32_t cNewEntries = (uint32_t)cReqMinEntries;
275#ifdef VBOX_WITH_STATISTICS
276 uint32_t const cOldEntries = pGVM->iomr0.s.cMmioStatsAllocation;
277 ASMCompilerBarrier();
278#else
279 uint32_t const cOldEntries = 0;
280#endif
281 AssertReturn(cNewEntries > cOldEntries, VERR_IOM_MMIO_IPE_1);
282 AssertReturn(pGVM->iom.s.cMmioStatsAllocation == cOldEntries, VERR_IOM_MMIO_IPE_1);
283 AssertReturn(pGVM->iom.s.cMmioStats <= cOldEntries, VERR_IOM_MMIO_IPE_2);
284#ifdef VBOX_WITH_STATISTICS
285 AssertReturn(!pGVM->iomr0.s.fMmioStatsFrozen, VERR_WRONG_ORDER);
286#endif
287
288 /*
289 * Allocate a new table, zero it and map it.
290 */
291#ifndef VBOX_WITH_STATISTICS
292 AssertFailedReturn(VERR_NOT_SUPPORTED);
293#else
294 uint32_t const cbNew = RT_ALIGN_32(cNewEntries * sizeof(IOMMMIOSTATSENTRY), HOST_PAGE_SIZE);
295 cNewEntries = cbNew / sizeof(IOMMMIOSTATSENTRY);
296
297 RTR0MEMOBJ hMemObj;
298 int rc = RTR0MemObjAllocPage(&hMemObj, cbNew, false /*fExecutable*/);
299 if (RT_SUCCESS(rc))
300 {
301 RT_BZERO(RTR0MemObjAddress(hMemObj), cbNew);
302
303 RTR0MEMOBJ hMapObj;
304 rc = RTR0MemObjMapUser(&hMapObj, hMemObj, (RTR3PTR)-1, HOST_PAGE_SIZE,
305 RTMEM_PROT_READ | RTMEM_PROT_WRITE, RTR0ProcHandleSelf());
306 if (RT_SUCCESS(rc))
307 {
308 PIOMMMIOSTATSENTRY pMmioStats = (PIOMMMIOSTATSENTRY)RTR0MemObjAddress(hMemObj);
309
310 /*
311 * Anything to copy over and free up?
312 */
313 if (pGVM->iomr0.s.paMmioStats)
314 memcpy(pMmioStats, pGVM->iomr0.s.paMmioStats, cOldEntries * sizeof(IOMMMIOSTATSENTRY));
315
316 /*
317 * Switch the memory handles.
318 */
319 RTR0MEMOBJ hTmp = pGVM->iomr0.s.hMmioStatsMapObj;
320 pGVM->iomr0.s.hMmioStatsMapObj = hMapObj;
321 hMapObj = hTmp;
322
323 hTmp = pGVM->iomr0.s.hMmioStatsMemObj;
324 pGVM->iomr0.s.hMmioStatsMemObj = hMemObj;
325 hMemObj = hTmp;
326
327 /*
328 * Update the variables.
329 */
330 pGVM->iomr0.s.paMmioStats = pMmioStats;
331 pGVM->iom.s.paMmioStats = RTR0MemObjAddressR3(pGVM->iomr0.s.hMmioStatsMapObj);
332 pGVM->iom.s.cMmioStatsAllocation = cNewEntries;
333 pGVM->iomr0.s.cMmioStatsAllocation = cNewEntries;
334
335 /*
336 * Free the old allocation.
337 */
338 RTR0MemObjFree(hMapObj, true /*fFreeMappings*/);
339 }
340 RTR0MemObjFree(hMemObj, true /*fFreeMappings*/);
341 }
342 return rc;
343#endif /* VBOX_WITH_STATISTICS */
344}
345
346
347/**
348 * Called after all devices has been instantiated to copy over the statistics
349 * indices to the ring-0 MMIO registration table.
350 *
351 * This simplifies keeping statistics for MMIO ranges that are ring-3 only.
352 *
353 * @returns VBox status code.
354 * @param pGVM The global (ring-0) VM structure.
355 * @thread EMT(0)
356 * @note Only callable at VM creation time.
357 */
358VMMR0_INT_DECL(int) IOMR0MmioSyncStatisticsIndices(PGVM pGVM)
359{
360 VM_ASSERT_EMT0_RETURN(pGVM, VERR_VM_THREAD_NOT_EMT);
361 VM_ASSERT_STATE_RETURN(pGVM, VMSTATE_CREATING, VERR_VM_INVALID_VM_STATE);
362
363#ifdef VBOX_WITH_STATISTICS
364 /*
365 * First, freeze the statistics array:
366 */
367 pGVM->iomr0.s.fMmioStatsFrozen = true;
368
369 /*
370 * Second, synchronize the indices:
371 */
372 uint32_t const cRegs = RT_MIN(pGVM->iom.s.cMmioRegs, pGVM->iomr0.s.cMmioAlloc);
373 uint32_t const cStatsAlloc = pGVM->iomr0.s.cMmioStatsAllocation;
374 PIOMMMIOENTRYR0 paMmioRegs = pGVM->iomr0.s.paMmioRegs;
375 IOMMMIOENTRYR3 const *paMmioRegsR3 = pGVM->iomr0.s.paMmioRing3Regs;
376 AssertReturn((paMmioRegs && paMmioRegsR3) || cRegs == 0, VERR_IOM_MMIO_IPE_3);
377
378 for (uint32_t i = 0 ; i < cRegs; i++)
379 {
380 uint16_t idxStats = paMmioRegsR3[i].idxStats;
381 paMmioRegs[i].idxStats = idxStats < cStatsAlloc ? idxStats : UINT16_MAX;
382 }
383
384#else
385 RT_NOREF(pGVM);
386#endif
387 return VINF_SUCCESS;
388}
389
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