VirtualBox

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

Last change on this file since 81335 was 81335, checked in by vboxsync, 5 years ago

IOM: More fixes for release builds. bugref:9218

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.8 KB
Line 
1/* $Id: IOMR0IoPort.cpp 81335 2019-10-18 00:09:19Z vboxsync $ */
2/** @file
3 * IOM - Host Context Ring 0, I/O ports.
4 */
5
6/*
7 * Copyright (C) 2006-2019 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
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 paRing3[i].idxSelf = (uint16_t)i;
209 paRing3[i].idxStats = UINT16_MAX;
210 }
211
212 /*
213 * Switch the memory handles.
214 */
215 RTR0MEMOBJ hTmp = pGVM->iomr0.s.hIoPortMapObj;
216 pGVM->iomr0.s.hIoPortMapObj = hMapObj;
217 hMapObj = hTmp;
218
219 hTmp = pGVM->iomr0.s.hIoPortMemObj;
220 pGVM->iomr0.s.hIoPortMemObj = hMemObj;
221 hMemObj = hTmp;
222
223 /*
224 * Update the variables.
225 */
226 pGVM->iomr0.s.paIoPortRegs = paRing0;
227 pGVM->iomr0.s.paIoPortRing3Regs = paRing3;
228 pGVM->iomr0.s.paIoPortLookup = paLookup;
229 pGVM->iom.s.paIoPortRegs = uAddrRing3;
230 pGVM->iom.s.paIoPortLookup = uAddrRing3 + cbRing3;
231 pGVM->iom.s.cIoPortAlloc = cNewEntries;
232 pGVM->iomr0.s.cIoPortAlloc = cNewEntries;
233
234 /*
235 * Free the old allocation.
236 */
237 RTR0MemObjFree(hMapObj, true /*fFreeMappings*/);
238 }
239 RTR0MemObjFree(hMemObj, true /*fFreeMappings*/);
240 }
241
242 return rc;
243}
244
245
246/**
247 * Grows the I/O port statistics table.
248 *
249 * @returns VBox status code.
250 * @param pGVM The global (ring-0) VM structure.
251 * @param cReqMinEntries The minimum growth (absolute).
252 * @thread EMT(0)
253 * @note Only callable at VM creation time.
254 */
255VMMR0_INT_DECL(int) IOMR0IoPortGrowStatisticsTable(PGVM pGVM, uint64_t cReqMinEntries)
256{
257 /*
258 * Validate input and state.
259 */
260 VM_ASSERT_EMT0_RETURN(pGVM, VERR_VM_THREAD_NOT_EMT);
261 VM_ASSERT_STATE_RETURN(pGVM, VMSTATE_CREATING, VERR_VM_INVALID_VM_STATE);
262 AssertReturn(cReqMinEntries <= _64K, VERR_IOM_TOO_MANY_IOPORT_REGISTRATIONS);
263 uint32_t cNewEntries = (uint32_t)cReqMinEntries;
264#ifdef VBOX_WITH_STATISTICS
265 uint32_t const cOldEntries = pGVM->iomr0.s.cIoPortStatsAllocation;
266 ASMCompilerBarrier();
267#else
268 uint32_t const cOldEntries = 0;
269#endif
270 AssertReturn(cNewEntries > cOldEntries, VERR_IOM_IOPORT_IPE_1);
271 AssertReturn(pGVM->iom.s.cIoPortStatsAllocation == cOldEntries, VERR_IOM_IOPORT_IPE_1);
272 AssertReturn(pGVM->iom.s.cIoPortStats <= cOldEntries, VERR_IOM_IOPORT_IPE_2);
273#ifdef VBOX_WITH_STATISTICS
274 AssertReturn(!pGVM->iomr0.s.fIoPortStatsFrozen, VERR_WRONG_ORDER);
275#endif
276
277 /*
278 * Allocate a new table, zero it and map it.
279 */
280#ifndef VBOX_WITH_STATISTICS
281 AssertFailedReturn(VERR_NOT_SUPPORTED);
282#else
283 uint32_t const cbNew = RT_ALIGN_32(cNewEntries * sizeof(IOMIOPORTSTATSENTRY), PAGE_SIZE);
284 cNewEntries = cbNew / sizeof(IOMIOPORTSTATSENTRY);
285
286 RTR0MEMOBJ hMemObj;
287 int rc = RTR0MemObjAllocPage(&hMemObj, cbNew, false /*fExecutable*/);
288 if (RT_SUCCESS(rc))
289 {
290 RT_BZERO(RTR0MemObjAddress(hMemObj), cbNew);
291
292 RTR0MEMOBJ hMapObj;
293 rc = RTR0MemObjMapUser(&hMapObj, hMemObj, (RTR3PTR)-1, PAGE_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE, RTR0ProcHandleSelf());
294 if (RT_SUCCESS(rc))
295 {
296 PIOMIOPORTSTATSENTRY pIoPortStats = (PIOMIOPORTSTATSENTRY)RTR0MemObjAddress(hMemObj);
297
298 /*
299 * Anything to copy over and free up?
300 */
301 if (pGVM->iomr0.s.paIoPortStats)
302 memcpy(pIoPortStats, pGVM->iomr0.s.paIoPortStats, cOldEntries * sizeof(IOMIOPORTSTATSENTRY));
303
304 /*
305 * Switch the memory handles.
306 */
307 RTR0MEMOBJ hTmp = pGVM->iomr0.s.hIoPortStatsMapObj;
308 pGVM->iomr0.s.hIoPortStatsMapObj = hMapObj;
309 hMapObj = hTmp;
310
311 hTmp = pGVM->iomr0.s.hIoPortStatsMemObj;
312 pGVM->iomr0.s.hIoPortStatsMemObj = hMemObj;
313 hMemObj = hTmp;
314
315 /*
316 * Update the variables.
317 */
318 pGVM->iomr0.s.paIoPortStats = pIoPortStats;
319 pGVM->iom.s.paIoPortStats = RTR0MemObjAddressR3(pGVM->iomr0.s.hIoPortStatsMapObj);
320 pGVM->iom.s.cIoPortStatsAllocation = cNewEntries;
321 pGVM->iomr0.s.cIoPortStatsAllocation = cNewEntries;
322
323 /*
324 * Free the old allocation.
325 */
326 RTR0MemObjFree(hMapObj, true /*fFreeMappings*/);
327 }
328 RTR0MemObjFree(hMemObj, true /*fFreeMappings*/);
329 }
330 return rc;
331#endif /* VBOX_WITH_STATISTICS */
332}
333
334/**
335 * Called after all devices has been instantiated to copy over the statistics
336 * indices to the ring-0 I/O port registration table.
337 *
338 * This simplifies keeping statistics for I/O port ranges that are ring-3 only.
339 *
340 * After this call, IOMR0IoPortGrowStatisticsTable() will stop working.
341 *
342 * @returns VBox status code.
343 * @param pGVM The global (ring-0) VM structure.
344 * @thread EMT(0)
345 * @note Only callable at VM creation time.
346 */
347VMMR0_INT_DECL(int) IOMR0IoPortSyncStatisticsIndices(PGVM pGVM)
348{
349 VM_ASSERT_EMT0_RETURN(pGVM, VERR_VM_THREAD_NOT_EMT);
350 VM_ASSERT_STATE_RETURN(pGVM, VMSTATE_CREATING, VERR_VM_INVALID_VM_STATE);
351
352#ifdef VBOX_WITH_STATISTICS
353 /*
354 * First, freeze the statistics array:
355 */
356 pGVM->iomr0.s.fIoPortStatsFrozen = true;
357
358 /*
359 * Second, synchronize the indices:
360 */
361 uint32_t const cRegs = RT_MIN(pGVM->iom.s.cIoPortRegs, pGVM->iomr0.s.cIoPortAlloc);
362 uint32_t const cStatsAlloc = pGVM->iomr0.s.cIoPortStatsAllocation;
363 PIOMIOPORTENTRYR0 paIoPortRegs = pGVM->iomr0.s.paIoPortRegs;
364 IOMIOPORTENTRYR3 const *paIoPortRegsR3 = pGVM->iomr0.s.paIoPortRing3Regs;
365 AssertReturn((paIoPortRegs && paIoPortRegsR3) || cRegs == 0, VERR_IOM_IOPORT_IPE_3);
366
367 for (uint32_t i = 0 ; i < cRegs; i++)
368 {
369 uint16_t idxStats = paIoPortRegsR3[i].idxStats;
370 paIoPortRegs[i].idxStats = idxStats < cStatsAlloc ? idxStats : UINT16_MAX;
371 }
372
373#else
374 RT_NOREF(pGVM);
375#endif
376 return VINF_SUCCESS;
377}
378
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