VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR0/IOMR0.cpp@ 80645

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

IOM: New I/O port registration code. [release build fix] bugref:9218

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.2 KB
Line 
1/* $Id: IOMR0.cpp 80645 2019-09-06 20:14:36Z vboxsync $ */
2/** @file
3 * IOM - Host Context Ring 0.
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 per-VM data for the IOM.
39 *
40 * This is called from under the GVMM lock, so it should only initialize the
41 * data so IOMR0CleanupVM and others will work smoothly.
42 *
43 * @param pGVM Pointer to the global VM structure.
44 */
45VMMR0_INT_DECL(void) IOMR0InitPerVMData(PGVM pGVM)
46{
47 AssertCompile(sizeof(pGVM->iom.s) <= sizeof(pGVM->iom.padding));
48 AssertCompile(sizeof(pGVM->iomr0.s) <= sizeof(pGVM->iomr0.padding));
49
50 pGVM->iomr0.s.hIoPortMapObj = NIL_RTR0MEMOBJ;
51 pGVM->iomr0.s.hIoPortMemObj = NIL_RTR0MEMOBJ;
52#ifdef VBOX_WITH_STATISTICS
53 pGVM->iomr0.s.hIoPortStatsMapObj = NIL_RTR0MEMOBJ;
54 pGVM->iomr0.s.hIoPortStatsMemObj = NIL_RTR0MEMOBJ;
55#endif
56}
57
58
59/**
60 * Cleans up any loose ends before the GVM structure is destroyed.
61 */
62VMMR0_INT_DECL(void) IOMR0CleanupVM(PGVM pGVM)
63{
64 RTR0MemObjFree(pGVM->iomr0.s.hIoPortMapObj, true /*fFreeMappings*/);
65 pGVM->iomr0.s.hIoPortMapObj = NIL_RTR0MEMOBJ;
66 RTR0MemObjFree(pGVM->iomr0.s.hIoPortMemObj, true /*fFreeMappings*/);
67 pGVM->iomr0.s.hIoPortMemObj = NIL_RTR0MEMOBJ;
68#ifdef VBOX_WITH_STATISTICS
69 RTR0MemObjFree(pGVM->iomr0.s.hIoPortStatsMapObj, true /*fFreeMappings*/);
70 pGVM->iomr0.s.hIoPortStatsMapObj = NIL_RTR0MEMOBJ;
71 RTR0MemObjFree(pGVM->iomr0.s.hIoPortStatsMemObj, true /*fFreeMappings*/);
72 pGVM->iomr0.s.hIoPortStatsMemObj = NIL_RTR0MEMOBJ;
73#endif
74}
75
76
77/**
78 * Implements PDMDEVHLPR0::pfnIoPortSetUpContext.
79 *
80 * @param pGVM The global (ring-0) VM structure.
81 * @param pDevIns The device instance.
82 * @param hIoPorts The I/O port handle (already registered in ring-3).
83 * @param pfnOut The OUT handler callback, optional.
84 * @param pfnIn The IN handler callback, optional.
85 * @param pfnOutStr The REP OUTS handler callback, optional.
86 * @param pfnInStr The REP INS handler callback, optional.
87 * @param pvUser User argument for the callbacks.
88 * @thread EMT(0)
89 * @note Only callable at VM creation time.
90 */
91VMMR0_INT_DECL(int) IOMR0IoPortSetUpContext(PGVM pGVM, PPDMDEVINS pDevIns, IOMIOPORTHANDLE hIoPorts,
92 PFNIOMIOPORTOUT pfnOut, PFNIOMIOPORTIN pfnIn,
93 PFNIOMIOPORTOUTSTRING pfnOutStr, PFNIOMIOPORTINSTRING pfnInStr, void *pvUser)
94{
95 /*
96 * Validate input and state.
97 */
98 VM_ASSERT_EMT0_RETURN(pGVM, VERR_VM_THREAD_NOT_EMT);
99 VM_ASSERT_STATE_RETURN(pGVM, VMSTATE_CREATING, VERR_VM_INVALID_VM_STATE);
100 AssertReturn(hIoPorts < pGVM->iomr0.s.cIoPortAlloc, VERR_IOM_INVALID_IOPORT_HANDLE);
101 AssertReturn(hIoPorts < pGVM->iom.s.cIoPortRegs, VERR_IOM_INVALID_IOPORT_HANDLE);
102 AssertPtrReturn(pDevIns, VERR_INVALID_HANDLE);
103 AssertReturn(pDevIns->pDevInsForR3 != NIL_RTR3PTR && !(pDevIns->pDevInsForR3 & PAGE_OFFSET_MASK), VERR_INVALID_PARAMETER);
104 AssertReturn(pGVM->iomr0.s.paIoPortRing3Regs[hIoPorts].pDevIns == pDevIns->pDevInsForR3, VERR_IOM_INVALID_IOPORT_HANDLE);
105 AssertReturn(pGVM->iomr0.s.paIoPortRegs[hIoPorts].pDevIns == NULL, VERR_WRONG_ORDER);
106
107 AssertReturn(pfnOut || pfnIn || pfnOutStr || pfnInStr, VERR_INVALID_PARAMETER);
108 AssertPtrNullReturn(pfnOut, VERR_INVALID_POINTER);
109 AssertPtrNullReturn(pfnIn, VERR_INVALID_POINTER);
110 AssertPtrNullReturn(pfnOutStr, VERR_INVALID_POINTER);
111 AssertPtrNullReturn(pfnInStr, VERR_INVALID_POINTER);
112
113 RTIOPORT const cPorts = pGVM->iomr0.s.paIoPortRing3Regs[hIoPorts].cPorts;
114 AssertMsgReturn(cPorts > 0 && cPorts <= _8K, ("cPorts=%s\n", cPorts), VERR_IOM_INVALID_IOPORT_HANDLE);
115
116 /*
117 * Do the job.
118 */
119 pGVM->iomr0.s.paIoPortRegs[hIoPorts].pvUser = pvUser;
120 pGVM->iomr0.s.paIoPortRegs[hIoPorts].pDevIns = pDevIns;
121 pGVM->iomr0.s.paIoPortRegs[hIoPorts].pfnOutCallback = pfnOut;
122 pGVM->iomr0.s.paIoPortRegs[hIoPorts].pfnInCallback = pfnIn;
123 pGVM->iomr0.s.paIoPortRegs[hIoPorts].pfnOutStrCallback = pfnOutStr;
124 pGVM->iomr0.s.paIoPortRegs[hIoPorts].pfnInStrCallback = pfnInStr;
125 pGVM->iomr0.s.paIoPortRegs[hIoPorts].cPorts = cPorts;
126#ifdef VBOX_WITH_STATISTICS
127 uint16_t const idxStats = pGVM->iomr0.s.paIoPortRing3Regs[hIoPorts].idxStats;
128 pGVM->iomr0.s.paIoPortRegs[hIoPorts].idxStats = (uint32_t)idxStats + cPorts <= pGVM->iomr0.s.cIoPortStatsAllocation
129 ? idxStats : UINT16_MAX;
130#else
131 pGVM->iomr0.s.paIoPortRegs[hIoPorts].idxStats = UINT16_MAX;
132#endif
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
274 /*
275 * Allocate a new table, zero it and map it.
276 */
277#ifndef VBOX_WITH_STATISTICS
278 AssertFailedReturn(VERR_NOT_SUPPORTED);
279#else
280 uint32_t const cbNew = RT_ALIGN_32(cNewEntries * sizeof(IOMIOPORTSTATSENTRY), PAGE_SIZE);
281 cNewEntries = cbNew / sizeof(IOMIOPORTSTATSENTRY);
282
283 RTR0MEMOBJ hMemObj;
284 int rc = RTR0MemObjAllocPage(&hMemObj, cbNew, false /*fExecutable*/);
285 if (RT_SUCCESS(rc))
286 {
287 RT_BZERO(RTR0MemObjAddress(hMemObj), cbNew);
288
289 RTR0MEMOBJ hMapObj;
290 rc = RTR0MemObjMapUser(&hMapObj, hMemObj, (RTR3PTR)-1, PAGE_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE, RTR0ProcHandleSelf());
291 if (RT_SUCCESS(rc))
292 {
293 PIOMIOPORTSTATSENTRY pIoPortStats = (PIOMIOPORTSTATSENTRY)RTR0MemObjAddress(hMemObj);
294
295 /*
296 * Anything to copy over and free up?
297 */
298 if (pGVM->iomr0.s.paIoPortStats)
299 memcpy(pIoPortStats, pGVM->iomr0.s.paIoPortStats, cOldEntries * sizeof(IOMIOPORTSTATSENTRY));
300
301 /*
302 * Switch the memory handles.
303 */
304 RTR0MEMOBJ hTmp = pGVM->iomr0.s.hIoPortStatsMapObj;
305 pGVM->iomr0.s.hIoPortStatsMapObj = hMapObj;
306 hMapObj = hTmp;
307
308 hTmp = pGVM->iomr0.s.hIoPortStatsMemObj;
309 pGVM->iomr0.s.hIoPortStatsMemObj = hMemObj;
310 hMemObj = hTmp;
311
312 /*
313 * Update the variables.
314 */
315 pGVM->iomr0.s.paIoPortStats = pIoPortStats;
316 pGVM->iom.s.paIoPortStats = RTR0MemObjAddressR3(pGVM->iomr0.s.hIoPortStatsMapObj);
317 pGVM->iom.s.cIoPortStatsAllocation = cNewEntries;
318 pGVM->iomr0.s.cIoPortStatsAllocation = cNewEntries;
319
320 /*
321 * Free the old allocation.
322 */
323 RTR0MemObjFree(hMapObj, true /*fFreeMappings*/);
324 }
325 RTR0MemObjFree(hMemObj, true /*fFreeMappings*/);
326 }
327 return rc;
328#endif /* VBOX_WITH_STATISTICS */
329}
330
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