VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR0/IOMR0IoPort.cpp@ 93444

Last change on this file since 93444 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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