VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR0/IOMR0IoPort.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: 15.2 KB
Line 
1/* $Id: IOMR0IoPort.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * IOM - Host Context Ring 0, I/O ports.
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_IOPORT
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 I/O port related members.
49 *
50 * @param pGVM Pointer to the global VM structure.
51 */
52void iomR0IoPortInitPerVMData(PGVM pGVM)
53{
54 pGVM->iomr0.s.hIoPortMapObj = NIL_RTR0MEMOBJ;
55 pGVM->iomr0.s.hIoPortMemObj = NIL_RTR0MEMOBJ;
56#ifdef VBOX_WITH_STATISTICS
57 pGVM->iomr0.s.hIoPortStatsMapObj = NIL_RTR0MEMOBJ;
58 pGVM->iomr0.s.hIoPortStatsMemObj = NIL_RTR0MEMOBJ;
59#endif
60}
61
62
63/**
64 * Cleans up I/O port related resources.
65 */
66void iomR0IoPortCleanupVM(PGVM pGVM)
67{
68 RTR0MemObjFree(pGVM->iomr0.s.hIoPortMapObj, true /*fFreeMappings*/);
69 pGVM->iomr0.s.hIoPortMapObj = NIL_RTR0MEMOBJ;
70 RTR0MemObjFree(pGVM->iomr0.s.hIoPortMemObj, true /*fFreeMappings*/);
71 pGVM->iomr0.s.hIoPortMemObj = NIL_RTR0MEMOBJ;
72#ifdef VBOX_WITH_STATISTICS
73 RTR0MemObjFree(pGVM->iomr0.s.hIoPortStatsMapObj, true /*fFreeMappings*/);
74 pGVM->iomr0.s.hIoPortStatsMapObj = NIL_RTR0MEMOBJ;
75 RTR0MemObjFree(pGVM->iomr0.s.hIoPortStatsMemObj, true /*fFreeMappings*/);
76 pGVM->iomr0.s.hIoPortStatsMemObj = NIL_RTR0MEMOBJ;
77#endif
78}
79
80
81/**
82 * Implements PDMDEVHLPR0::pfnIoPortSetUpContext.
83 *
84 * @param pGVM The global (ring-0) VM structure.
85 * @param pDevIns The device instance.
86 * @param hIoPorts The I/O port handle (already registered in ring-3).
87 * @param pfnOut The OUT handler callback, optional.
88 * @param pfnIn The IN handler callback, optional.
89 * @param pfnOutStr The REP OUTS handler callback, optional.
90 * @param pfnInStr The REP INS 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) IOMR0IoPortSetUpContext(PGVM pGVM, PPDMDEVINS pDevIns, IOMIOPORTHANDLE hIoPorts,
96 PFNIOMIOPORTNEWOUT pfnOut, PFNIOMIOPORTNEWIN pfnIn,
97 PFNIOMIOPORTNEWOUTSTRING pfnOutStr, PFNIOMIOPORTNEWINSTRING pfnInStr, void *pvUser)
98{
99 /*
100 * Validate input and state.
101 */
102 VM_ASSERT_EMT0_RETURN(pGVM, VERR_VM_THREAD_NOT_EMT);
103 VM_ASSERT_STATE_RETURN(pGVM, VMSTATE_CREATING, VERR_VM_INVALID_VM_STATE);
104 AssertReturn(hIoPorts < pGVM->iomr0.s.cIoPortAlloc, VERR_IOM_INVALID_IOPORT_HANDLE);
105 AssertReturn(hIoPorts < pGVM->iom.s.cIoPortRegs, VERR_IOM_INVALID_IOPORT_HANDLE);
106 AssertPtrReturn(pDevIns, VERR_INVALID_HANDLE);
107 AssertReturn(pDevIns->pDevInsForR3 != NIL_RTR3PTR && !(pDevIns->pDevInsForR3 & HOST_PAGE_OFFSET_MASK), VERR_INVALID_PARAMETER);
108 AssertReturn(pGVM->iomr0.s.paIoPortRing3Regs[hIoPorts].pDevIns == pDevIns->pDevInsForR3, VERR_IOM_INVALID_IOPORT_HANDLE);
109 AssertReturn(pGVM->iomr0.s.paIoPortRegs[hIoPorts].pDevIns == NULL, VERR_WRONG_ORDER);
110 Assert(pGVM->iomr0.s.paIoPortRegs[hIoPorts].idxSelf == hIoPorts);
111
112 AssertReturn(pfnOut || pfnIn || pfnOutStr || pfnInStr, VERR_INVALID_PARAMETER);
113 AssertPtrNullReturn(pfnOut, VERR_INVALID_POINTER);
114 AssertPtrNullReturn(pfnIn, VERR_INVALID_POINTER);
115 AssertPtrNullReturn(pfnOutStr, VERR_INVALID_POINTER);
116 AssertPtrNullReturn(pfnInStr, VERR_INVALID_POINTER);
117
118 uint16_t const fFlags = pGVM->iomr0.s.paIoPortRing3Regs[hIoPorts].fFlags;
119 RTIOPORT const cPorts = pGVM->iomr0.s.paIoPortRing3Regs[hIoPorts].cPorts;
120 AssertMsgReturn(cPorts > 0 && cPorts <= _8K, ("cPorts=%s\n", cPorts), VERR_IOM_INVALID_IOPORT_HANDLE);
121
122 /*
123 * Do the job.
124 */
125 pGVM->iomr0.s.paIoPortRegs[hIoPorts].pvUser = pvUser;
126 pGVM->iomr0.s.paIoPortRegs[hIoPorts].pDevIns = pDevIns;
127 pGVM->iomr0.s.paIoPortRegs[hIoPorts].pfnOutCallback = pfnOut;
128 pGVM->iomr0.s.paIoPortRegs[hIoPorts].pfnInCallback = pfnIn;
129 pGVM->iomr0.s.paIoPortRegs[hIoPorts].pfnOutStrCallback = pfnOutStr;
130 pGVM->iomr0.s.paIoPortRegs[hIoPorts].pfnInStrCallback = pfnInStr;
131 pGVM->iomr0.s.paIoPortRegs[hIoPorts].cPorts = cPorts;
132 pGVM->iomr0.s.paIoPortRegs[hIoPorts].fFlags = fFlags;
133#ifdef VBOX_WITH_STATISTICS
134 uint16_t const idxStats = pGVM->iomr0.s.paIoPortRing3Regs[hIoPorts].idxStats;
135 pGVM->iomr0.s.paIoPortRegs[hIoPorts].idxStats = (uint32_t)idxStats + cPorts <= pGVM->iomr0.s.cIoPortStatsAllocation
136 ? idxStats : UINT16_MAX;
137#else
138 pGVM->iomr0.s.paIoPortRegs[hIoPorts].idxStats = UINT16_MAX;
139#endif
140
141 pGVM->iomr0.s.paIoPortRing3Regs[hIoPorts].fRing0 = true;
142
143 return VINF_SUCCESS;
144}
145
146
147/**
148 * Grows the I/O port registration (all contexts) and lookup tables.
149 *
150 * @returns VBox status code.
151 * @param pGVM The global (ring-0) VM structure.
152 * @param cReqMinEntries The minimum growth (absolute).
153 * @thread EMT(0)
154 * @note Only callable at VM creation time.
155 */
156VMMR0_INT_DECL(int) IOMR0IoPortGrowRegistrationTables(PGVM pGVM, uint64_t cReqMinEntries)
157{
158 /*
159 * Validate input and state.
160 */
161 VM_ASSERT_EMT0_RETURN(pGVM, VERR_VM_THREAD_NOT_EMT);
162 VM_ASSERT_STATE_RETURN(pGVM, VMSTATE_CREATING, VERR_VM_INVALID_VM_STATE);
163 AssertReturn(cReqMinEntries <= _4K, VERR_IOM_TOO_MANY_IOPORT_REGISTRATIONS);
164 uint32_t cNewEntries = (uint32_t)cReqMinEntries;
165 AssertReturn(cNewEntries >= pGVM->iom.s.cIoPortAlloc, VERR_IOM_IOPORT_IPE_1);
166 uint32_t const cOldEntries = pGVM->iomr0.s.cIoPortAlloc;
167 ASMCompilerBarrier();
168 AssertReturn(cNewEntries >= cOldEntries, VERR_IOM_IOPORT_IPE_2);
169 AssertReturn(pGVM->iom.s.cIoPortRegs >= pGVM->iomr0.s.cIoPortMax, VERR_IOM_IOPORT_IPE_3);
170
171 /*
172 * Allocate the new tables. We use a single allocation for the three tables (ring-0,
173 * ring-3, lookup) and does a partial mapping of the result to ring-3.
174 */
175 uint32_t const cbRing0 = RT_ALIGN_32(cNewEntries * sizeof(IOMIOPORTENTRYR0), HOST_PAGE_SIZE);
176 uint32_t const cbRing3 = RT_ALIGN_32(cNewEntries * sizeof(IOMIOPORTENTRYR3), HOST_PAGE_SIZE);
177 uint32_t const cbShared = RT_ALIGN_32(cNewEntries * sizeof(IOMIOPORTLOOKUPENTRY), HOST_PAGE_SIZE);
178 uint32_t const cbNew = cbRing0 + cbRing3 + cbShared;
179
180 /* Use the rounded up space as best we can. */
181 cNewEntries = RT_MIN(RT_MIN(cbRing0 / sizeof(IOMIOPORTENTRYR0), cbRing3 / sizeof(IOMIOPORTENTRYR3)),
182 cbShared / sizeof(IOMIOPORTLOOKUPENTRY));
183
184 RTR0MEMOBJ hMemObj;
185 int rc = RTR0MemObjAllocPage(&hMemObj, cbNew, false /*fExecutable*/);
186 if (RT_SUCCESS(rc))
187 {
188 /*
189 * Zero and map it.
190 */
191 RT_BZERO(RTR0MemObjAddress(hMemObj), cbNew);
192
193 RTR0MEMOBJ hMapObj;
194 rc = RTR0MemObjMapUserEx(&hMapObj, hMemObj, (RTR3PTR)-1, HOST_PAGE_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE,
195 RTR0ProcHandleSelf(), cbRing0, cbNew - cbRing0);
196 if (RT_SUCCESS(rc))
197 {
198 PIOMIOPORTENTRYR0 const paRing0 = (PIOMIOPORTENTRYR0)RTR0MemObjAddress(hMemObj);
199 PIOMIOPORTENTRYR3 const paRing3 = (PIOMIOPORTENTRYR3)((uintptr_t)paRing0 + cbRing0);
200 PIOMIOPORTLOOKUPENTRY const paLookup = (PIOMIOPORTLOOKUPENTRY)((uintptr_t)paRing3 + cbRing3);
201 RTR3UINTPTR const uAddrRing3 = RTR0MemObjAddressR3(hMapObj);
202
203 /*
204 * Copy over the old info and initialize the idxSelf and idxStats members.
205 */
206 if (pGVM->iomr0.s.paIoPortRegs != NULL)
207 {
208 memcpy(paRing0, pGVM->iomr0.s.paIoPortRegs, sizeof(paRing0[0]) * cOldEntries);
209 memcpy(paRing3, pGVM->iomr0.s.paIoPortRing3Regs, sizeof(paRing3[0]) * cOldEntries);
210 memcpy(paLookup, pGVM->iomr0.s.paIoPortLookup, sizeof(paLookup[0]) * cOldEntries);
211 }
212
213 size_t i = cbRing0 / sizeof(*paRing0);
214 while (i-- > cOldEntries)
215 {
216 paRing0[i].idxSelf = (uint16_t)i;
217 paRing0[i].idxStats = UINT16_MAX;
218 }
219 i = cbRing3 / sizeof(*paRing3);
220 while (i-- > cOldEntries)
221 {
222 paRing3[i].idxSelf = (uint16_t)i;
223 paRing3[i].idxStats = UINT16_MAX;
224 }
225
226 /*
227 * Switch the memory handles.
228 */
229 RTR0MEMOBJ hTmp = pGVM->iomr0.s.hIoPortMapObj;
230 pGVM->iomr0.s.hIoPortMapObj = hMapObj;
231 hMapObj = hTmp;
232
233 hTmp = pGVM->iomr0.s.hIoPortMemObj;
234 pGVM->iomr0.s.hIoPortMemObj = hMemObj;
235 hMemObj = hTmp;
236
237 /*
238 * Update the variables.
239 */
240 pGVM->iomr0.s.paIoPortRegs = paRing0;
241 pGVM->iomr0.s.paIoPortRing3Regs = paRing3;
242 pGVM->iomr0.s.paIoPortLookup = paLookup;
243 pGVM->iom.s.paIoPortRegs = uAddrRing3;
244 pGVM->iom.s.paIoPortLookup = uAddrRing3 + cbRing3;
245 pGVM->iom.s.cIoPortAlloc = cNewEntries;
246 pGVM->iomr0.s.cIoPortAlloc = cNewEntries;
247
248 /*
249 * Free the old allocation.
250 */
251 RTR0MemObjFree(hMapObj, true /*fFreeMappings*/);
252 }
253 RTR0MemObjFree(hMemObj, true /*fFreeMappings*/);
254 }
255
256 return rc;
257}
258
259
260/**
261 * Grows the I/O port statistics table.
262 *
263 * @returns VBox status code.
264 * @param pGVM The global (ring-0) VM structure.
265 * @param cReqMinEntries The minimum growth (absolute).
266 * @thread EMT(0)
267 * @note Only callable at VM creation time.
268 */
269VMMR0_INT_DECL(int) IOMR0IoPortGrowStatisticsTable(PGVM pGVM, uint64_t cReqMinEntries)
270{
271 /*
272 * Validate input and state.
273 */
274 VM_ASSERT_EMT0_RETURN(pGVM, VERR_VM_THREAD_NOT_EMT);
275 VM_ASSERT_STATE_RETURN(pGVM, VMSTATE_CREATING, VERR_VM_INVALID_VM_STATE);
276 AssertReturn(cReqMinEntries <= _64K, VERR_IOM_TOO_MANY_IOPORT_REGISTRATIONS);
277 uint32_t cNewEntries = (uint32_t)cReqMinEntries;
278#ifdef VBOX_WITH_STATISTICS
279 uint32_t const cOldEntries = pGVM->iomr0.s.cIoPortStatsAllocation;
280 ASMCompilerBarrier();
281#else
282 uint32_t const cOldEntries = 0;
283#endif
284 AssertReturn(cNewEntries > cOldEntries, VERR_IOM_IOPORT_IPE_1);
285 AssertReturn(pGVM->iom.s.cIoPortStatsAllocation == cOldEntries, VERR_IOM_IOPORT_IPE_1);
286 AssertReturn(pGVM->iom.s.cIoPortStats <= cOldEntries, VERR_IOM_IOPORT_IPE_2);
287#ifdef VBOX_WITH_STATISTICS
288 AssertReturn(!pGVM->iomr0.s.fIoPortStatsFrozen, VERR_WRONG_ORDER);
289#endif
290
291 /*
292 * Allocate a new table, zero it and map it.
293 */
294#ifndef VBOX_WITH_STATISTICS
295 AssertFailedReturn(VERR_NOT_SUPPORTED);
296#else
297 uint32_t const cbNew = RT_ALIGN_32(cNewEntries * sizeof(IOMIOPORTSTATSENTRY), HOST_PAGE_SIZE);
298 cNewEntries = cbNew / sizeof(IOMIOPORTSTATSENTRY);
299
300 RTR0MEMOBJ hMemObj;
301 int rc = RTR0MemObjAllocPage(&hMemObj, cbNew, false /*fExecutable*/);
302 if (RT_SUCCESS(rc))
303 {
304 RT_BZERO(RTR0MemObjAddress(hMemObj), cbNew);
305
306 RTR0MEMOBJ hMapObj;
307 rc = RTR0MemObjMapUser(&hMapObj, hMemObj, (RTR3PTR)-1, HOST_PAGE_SIZE,
308 RTMEM_PROT_READ | RTMEM_PROT_WRITE, RTR0ProcHandleSelf());
309 if (RT_SUCCESS(rc))
310 {
311 PIOMIOPORTSTATSENTRY pIoPortStats = (PIOMIOPORTSTATSENTRY)RTR0MemObjAddress(hMemObj);
312
313 /*
314 * Anything to copy over and free up?
315 */
316 if (pGVM->iomr0.s.paIoPortStats)
317 memcpy(pIoPortStats, pGVM->iomr0.s.paIoPortStats, cOldEntries * sizeof(IOMIOPORTSTATSENTRY));
318
319 /*
320 * Switch the memory handles.
321 */
322 RTR0MEMOBJ hTmp = pGVM->iomr0.s.hIoPortStatsMapObj;
323 pGVM->iomr0.s.hIoPortStatsMapObj = hMapObj;
324 hMapObj = hTmp;
325
326 hTmp = pGVM->iomr0.s.hIoPortStatsMemObj;
327 pGVM->iomr0.s.hIoPortStatsMemObj = hMemObj;
328 hMemObj = hTmp;
329
330 /*
331 * Update the variables.
332 */
333 pGVM->iomr0.s.paIoPortStats = pIoPortStats;
334 pGVM->iom.s.paIoPortStats = RTR0MemObjAddressR3(pGVM->iomr0.s.hIoPortStatsMapObj);
335 pGVM->iom.s.cIoPortStatsAllocation = cNewEntries;
336 pGVM->iomr0.s.cIoPortStatsAllocation = cNewEntries;
337
338 /*
339 * Free the old allocation.
340 */
341 RTR0MemObjFree(hMapObj, true /*fFreeMappings*/);
342 }
343 RTR0MemObjFree(hMemObj, true /*fFreeMappings*/);
344 }
345 return rc;
346#endif /* VBOX_WITH_STATISTICS */
347}
348
349/**
350 * Called after all devices has been instantiated to copy over the statistics
351 * indices to the ring-0 I/O port registration table.
352 *
353 * This simplifies keeping statistics for I/O port ranges that are ring-3 only.
354 *
355 * After this call, IOMR0IoPortGrowStatisticsTable() will stop working.
356 *
357 * @returns VBox status code.
358 * @param pGVM The global (ring-0) VM structure.
359 * @thread EMT(0)
360 * @note Only callable at VM creation time.
361 */
362VMMR0_INT_DECL(int) IOMR0IoPortSyncStatisticsIndices(PGVM pGVM)
363{
364 VM_ASSERT_EMT0_RETURN(pGVM, VERR_VM_THREAD_NOT_EMT);
365 VM_ASSERT_STATE_RETURN(pGVM, VMSTATE_CREATING, VERR_VM_INVALID_VM_STATE);
366
367#ifdef VBOX_WITH_STATISTICS
368 /*
369 * First, freeze the statistics array:
370 */
371 pGVM->iomr0.s.fIoPortStatsFrozen = true;
372
373 /*
374 * Second, synchronize the indices:
375 */
376 uint32_t const cRegs = RT_MIN(pGVM->iom.s.cIoPortRegs, pGVM->iomr0.s.cIoPortAlloc);
377 uint32_t const cStatsAlloc = pGVM->iomr0.s.cIoPortStatsAllocation;
378 PIOMIOPORTENTRYR0 paIoPortRegs = pGVM->iomr0.s.paIoPortRegs;
379 IOMIOPORTENTRYR3 const *paIoPortRegsR3 = pGVM->iomr0.s.paIoPortRing3Regs;
380 AssertReturn((paIoPortRegs && paIoPortRegsR3) || cRegs == 0, VERR_IOM_IOPORT_IPE_3);
381
382 for (uint32_t i = 0 ; i < cRegs; i++)
383 {
384 uint16_t idxStats = paIoPortRegsR3[i].idxStats;
385 paIoPortRegs[i].idxStats = idxStats < cStatsAlloc ? idxStats : UINT16_MAX;
386 }
387
388#else
389 RT_NOREF(pGVM);
390#endif
391 return VINF_SUCCESS;
392}
393
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