VirtualBox

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

Last change on this file since 95248 was 93554, checked in by vboxsync, 3 years ago

VMM: Changed PAGE_SIZE -> GUEST_PAGE_SIZE / HOST_PAGE_SIZE, PAGE_SHIFT -> GUEST_PAGE_SHIFT / HOST_PAGE_SHIFT, and PAGE_OFFSET_MASK -> GUEST_PAGE_OFFSET_MASK / HOST_PAGE_OFFSET_MASK. Also removed most usage of ASMMemIsZeroPage and ASMMemZeroPage since the host and guest page size doesn't need to be the same any more. Some work left to do in the page pool code. bugref:9898

  • 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 93554 2022-02-02 22:57:02Z 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 & HOST_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), HOST_PAGE_SIZE);
166 uint32_t const cbRing3 = RT_ALIGN_32(cNewEntries * sizeof(IOMIOPORTENTRYR3), HOST_PAGE_SIZE);
167 uint32_t const cbShared = RT_ALIGN_32(cNewEntries * sizeof(IOMIOPORTLOOKUPENTRY), HOST_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, HOST_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), HOST_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, HOST_PAGE_SIZE,
298 RTMEM_PROT_READ | RTMEM_PROT_WRITE, RTR0ProcHandleSelf());
299 if (RT_SUCCESS(rc))
300 {
301 PIOMIOPORTSTATSENTRY pIoPortStats = (PIOMIOPORTSTATSENTRY)RTR0MemObjAddress(hMemObj);
302
303 /*
304 * Anything to copy over and free up?
305 */
306 if (pGVM->iomr0.s.paIoPortStats)
307 memcpy(pIoPortStats, pGVM->iomr0.s.paIoPortStats, cOldEntries * sizeof(IOMIOPORTSTATSENTRY));
308
309 /*
310 * Switch the memory handles.
311 */
312 RTR0MEMOBJ hTmp = pGVM->iomr0.s.hIoPortStatsMapObj;
313 pGVM->iomr0.s.hIoPortStatsMapObj = hMapObj;
314 hMapObj = hTmp;
315
316 hTmp = pGVM->iomr0.s.hIoPortStatsMemObj;
317 pGVM->iomr0.s.hIoPortStatsMemObj = hMemObj;
318 hMemObj = hTmp;
319
320 /*
321 * Update the variables.
322 */
323 pGVM->iomr0.s.paIoPortStats = pIoPortStats;
324 pGVM->iom.s.paIoPortStats = RTR0MemObjAddressR3(pGVM->iomr0.s.hIoPortStatsMapObj);
325 pGVM->iom.s.cIoPortStatsAllocation = cNewEntries;
326 pGVM->iomr0.s.cIoPortStatsAllocation = cNewEntries;
327
328 /*
329 * Free the old allocation.
330 */
331 RTR0MemObjFree(hMapObj, true /*fFreeMappings*/);
332 }
333 RTR0MemObjFree(hMemObj, true /*fFreeMappings*/);
334 }
335 return rc;
336#endif /* VBOX_WITH_STATISTICS */
337}
338
339/**
340 * Called after all devices has been instantiated to copy over the statistics
341 * indices to the ring-0 I/O port registration table.
342 *
343 * This simplifies keeping statistics for I/O port ranges that are ring-3 only.
344 *
345 * After this call, IOMR0IoPortGrowStatisticsTable() will stop working.
346 *
347 * @returns VBox status code.
348 * @param pGVM The global (ring-0) VM structure.
349 * @thread EMT(0)
350 * @note Only callable at VM creation time.
351 */
352VMMR0_INT_DECL(int) IOMR0IoPortSyncStatisticsIndices(PGVM pGVM)
353{
354 VM_ASSERT_EMT0_RETURN(pGVM, VERR_VM_THREAD_NOT_EMT);
355 VM_ASSERT_STATE_RETURN(pGVM, VMSTATE_CREATING, VERR_VM_INVALID_VM_STATE);
356
357#ifdef VBOX_WITH_STATISTICS
358 /*
359 * First, freeze the statistics array:
360 */
361 pGVM->iomr0.s.fIoPortStatsFrozen = true;
362
363 /*
364 * Second, synchronize the indices:
365 */
366 uint32_t const cRegs = RT_MIN(pGVM->iom.s.cIoPortRegs, pGVM->iomr0.s.cIoPortAlloc);
367 uint32_t const cStatsAlloc = pGVM->iomr0.s.cIoPortStatsAllocation;
368 PIOMIOPORTENTRYR0 paIoPortRegs = pGVM->iomr0.s.paIoPortRegs;
369 IOMIOPORTENTRYR3 const *paIoPortRegsR3 = pGVM->iomr0.s.paIoPortRing3Regs;
370 AssertReturn((paIoPortRegs && paIoPortRegsR3) || cRegs == 0, VERR_IOM_IOPORT_IPE_3);
371
372 for (uint32_t i = 0 ; i < cRegs; i++)
373 {
374 uint16_t idxStats = paIoPortRegsR3[i].idxStats;
375 paIoPortRegs[i].idxStats = idxStats < cStatsAlloc ? idxStats : UINT16_MAX;
376 }
377
378#else
379 RT_NOREF(pGVM);
380#endif
381 return VINF_SUCCESS;
382}
383
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