VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/IOMR3IoPort.cpp@ 93609

Last change on this file since 93609 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 Id Revision
File size: 30.3 KB
Line 
1/* $Id: IOMR3IoPort.cpp 93554 2022-02-02 22:57:02Z vboxsync $ */
2/** @file
3 * IOM - Input / Output Monitor, I/O port related APIs.
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 <VBox/sup.h>
25#include <VBox/vmm/mm.h>
26#include <VBox/vmm/stam.h>
27#include <VBox/vmm/dbgf.h>
28#include <VBox/vmm/pdmapi.h>
29#include <VBox/vmm/pdmdev.h>
30#include "IOMInternal.h"
31#include <VBox/vmm/vm.h>
32
33#include <VBox/param.h>
34#include <iprt/assert.h>
35#include <iprt/mem.h>
36#include <iprt/string.h>
37#include <VBox/log.h>
38#include <VBox/err.h>
39
40#include "IOMInline.h"
41
42
43#ifdef VBOX_WITH_STATISTICS
44
45/**
46 * Register statistics for an I/O port entry.
47 */
48void iomR3IoPortRegStats(PVM pVM, PIOMIOPORTENTRYR3 pRegEntry)
49{
50 bool const fDoRZ = pRegEntry->fRing0 || pRegEntry->fRawMode;
51 PIOMIOPORTSTATSENTRY pStats = &pVM->iom.s.paIoPortStats[pRegEntry->idxStats];
52 PCIOMIOPORTDESC pExtDesc = pRegEntry->paExtDescs;
53 unsigned uPort = pRegEntry->uPort;
54 unsigned const uFirstPort = uPort;
55 unsigned const uEndPort = uPort + pRegEntry->cPorts;
56
57 /* Register a dummy statistics for the prefix. */
58 char szName[80];
59 size_t cchPrefix;
60 if (uFirstPort < uEndPort - 1)
61 cchPrefix = RTStrPrintf(szName, sizeof(szName), "/IOM/IoPorts/%04x-%04x", uFirstPort, uEndPort - 1);
62 else
63 cchPrefix = RTStrPrintf(szName, sizeof(szName), "/IOM/IoPorts/%04x", uPort);
64 const char *pszDesc = pRegEntry->pszDesc;
65 char *pszFreeDesc = NULL;
66 if (pRegEntry->pDevIns && pRegEntry->pDevIns->iInstance > 0 && pszDesc)
67 pszDesc = pszFreeDesc = RTStrAPrintf2("%u / %s", pRegEntry->pDevIns->iInstance, pszDesc);
68 int rc = STAMR3Register(pVM, &pStats->Total, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, szName,
69 STAMUNIT_NONE, pRegEntry->pszDesc);
70 AssertRC(rc);
71 RTStrFree(pszFreeDesc);
72
73 /* Register stats for each port under it */
74 do
75 {
76 size_t cchBaseNm;
77 if (uFirstPort < uEndPort - 1)
78 cchBaseNm = cchPrefix + RTStrPrintf(&szName[cchPrefix], sizeof(szName) - cchPrefix, "/%04x-", uPort);
79 else
80 {
81 szName[cchPrefix] = '/';
82 cchBaseNm = cchPrefix + 1;
83 }
84
85# define SET_NM_SUFFIX(a_sz) memcpy(&szName[cchBaseNm], a_sz, sizeof(a_sz));
86 const char * const pszInDesc = pExtDesc ? pExtDesc->pszIn : NULL;
87 const char * const pszOutDesc = pExtDesc ? pExtDesc->pszOut : NULL;
88
89 /* register the statistics counters. */
90 SET_NM_SUFFIX("In-R3");
91 rc = STAMR3Register(pVM, &pStats->InR3, STAMTYPE_COUNTER, STAMVISIBILITY_USED, szName, STAMUNIT_OCCURENCES, pszInDesc); AssertRC(rc);
92 SET_NM_SUFFIX("Out-R3");
93 rc = STAMR3Register(pVM, &pStats->OutR3, STAMTYPE_COUNTER, STAMVISIBILITY_USED, szName, STAMUNIT_OCCURENCES, pszOutDesc); AssertRC(rc);
94 if (fDoRZ)
95 {
96 SET_NM_SUFFIX("In-RZ");
97 rc = STAMR3Register(pVM, &pStats->InRZ, STAMTYPE_COUNTER, STAMVISIBILITY_USED, szName, STAMUNIT_OCCURENCES, pszInDesc); AssertRC(rc);
98 SET_NM_SUFFIX("Out-RZ");
99 rc = STAMR3Register(pVM, &pStats->OutRZ, STAMTYPE_COUNTER, STAMVISIBILITY_USED, szName, STAMUNIT_OCCURENCES, pszOutDesc); AssertRC(rc);
100 SET_NM_SUFFIX("In-RZtoR3");
101 rc = STAMR3Register(pVM, &pStats->InRZToR3, STAMTYPE_COUNTER, STAMVISIBILITY_USED, szName, STAMUNIT_OCCURENCES, NULL); AssertRC(rc);
102 SET_NM_SUFFIX("Out-RZtoR3");
103 rc = STAMR3Register(pVM, &pStats->OutRZToR3, STAMTYPE_COUNTER, STAMVISIBILITY_USED, szName, STAMUNIT_OCCURENCES, NULL); AssertRC(rc);
104 }
105
106 /* Profiling */
107 SET_NM_SUFFIX("In-R3-Prof");
108 rc = STAMR3Register(pVM, &pStats->ProfInR3, STAMTYPE_PROFILE, STAMVISIBILITY_USED, szName, STAMUNIT_TICKS_PER_CALL, pszInDesc); AssertRC(rc);
109 SET_NM_SUFFIX("Out-R3-Prof");
110 rc = STAMR3Register(pVM, &pStats->ProfOutR3, STAMTYPE_PROFILE, STAMVISIBILITY_USED, szName, STAMUNIT_TICKS_PER_CALL, pszOutDesc); AssertRC(rc);
111 if (fDoRZ)
112 {
113 SET_NM_SUFFIX("In-RZ-Prof");
114 rc = STAMR3Register(pVM, &pStats->ProfInRZ, STAMTYPE_PROFILE, STAMVISIBILITY_USED, szName, STAMUNIT_TICKS_PER_CALL, pszInDesc); AssertRC(rc);
115 SET_NM_SUFFIX("Out-RZ-Prof");
116 rc = STAMR3Register(pVM, &pStats->ProfOutRZ, STAMTYPE_PROFILE, STAMVISIBILITY_USED, szName, STAMUNIT_TICKS_PER_CALL, pszOutDesc); AssertRC(rc);
117 }
118
119 pStats++;
120 uPort++;
121 if (pExtDesc)
122 pExtDesc = pszInDesc || pszOutDesc ? pExtDesc + 1 : NULL;
123 } while (uPort < uEndPort);
124}
125
126
127/**
128 * Deregister statistics for an I/O port entry.
129 */
130static void iomR3IoPortDeregStats(PVM pVM, PIOMIOPORTENTRYR3 pRegEntry, unsigned uPort)
131{
132 char szPrefix[80];
133 size_t cchPrefix;
134 if (pRegEntry->cPorts > 1)
135 cchPrefix = RTStrPrintf(szPrefix, sizeof(szPrefix), "/IOM/IoPorts/%04x-%04x", uPort, uPort + pRegEntry->cPorts - 1);
136 else
137 cchPrefix = RTStrPrintf(szPrefix, sizeof(szPrefix), "/IOM/IoPorts/%04x", uPort);
138 STAMR3DeregisterByPrefix(pVM->pUVM, szPrefix);
139}
140
141#endif /* VBOX_WITH_STATISTICS */
142
143
144/**
145 * @callback_method_impl{FNIOMIOPORTNEWIN,
146 * Dummy Port I/O Handler for IN operations.}
147 */
148static DECLCALLBACK(VBOXSTRICTRC)
149iomR3IOPortDummyNewIn(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
150{
151 NOREF(pDevIns); NOREF(pvUser); NOREF(Port);
152 switch (cb)
153 {
154 case 1: *pu32 = 0xff; break;
155 case 2: *pu32 = 0xffff; break;
156 case 4: *pu32 = UINT32_C(0xffffffff); break;
157 default:
158 AssertReleaseMsgFailed(("cb=%d\n", cb));
159 return VERR_IOM_IOPORT_IPE_2;
160 }
161 return VINF_SUCCESS;
162}
163
164
165/**
166 * @callback_method_impl{FNIOMIOPORTNEWINSTRING,
167 * Dummy Port I/O Handler for string IN operations.}
168 */
169static DECLCALLBACK(VBOXSTRICTRC)
170iomR3IOPortDummyNewInStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint8_t *pbDst, uint32_t *pcTransfer, unsigned cb)
171{
172 NOREF(pDevIns); NOREF(pvUser); NOREF(Port); NOREF(pbDst); NOREF(pcTransfer); NOREF(cb);
173 return VINF_SUCCESS;
174}
175
176
177/**
178 * @callback_method_impl{FNIOMIOPORTNEWOUT,
179 * Dummy Port I/O Handler for OUT operations.}
180 */
181static DECLCALLBACK(VBOXSTRICTRC)
182iomR3IOPortDummyNewOut(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
183{
184 NOREF(pDevIns); NOREF(pvUser); NOREF(Port); NOREF(u32); NOREF(cb);
185 return VINF_SUCCESS;
186}
187
188
189/**
190 * @callback_method_impl{FNIOMIOPORTNEWOUTSTRING,
191 * Dummy Port I/O Handler for string OUT operations.}
192 */
193static DECLCALLBACK(VBOXSTRICTRC)
194iomR3IOPortDummyNewOutStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint8_t const *pbSrc, uint32_t *pcTransfer, unsigned cb)
195{
196 NOREF(pDevIns); NOREF(pvUser); NOREF(Port); NOREF(pbSrc); NOREF(pcTransfer); NOREF(cb);
197 return VINF_SUCCESS;
198}
199
200
201#ifdef VBOX_WITH_STATISTICS
202/**
203 * Grows the statistics table.
204 *
205 * @returns VBox status code.
206 * @param pVM The cross context VM structure.
207 * @param cNewEntries The minimum number of new entrie.
208 * @see IOMR0IoPortGrowStatisticsTable
209 */
210static int iomR3IoPortGrowStatisticsTable(PVM pVM, uint32_t cNewEntries)
211{
212 AssertReturn(cNewEntries <= _64K, VERR_IOM_TOO_MANY_IOPORT_REGISTRATIONS);
213
214 int rc;
215 if (!SUPR3IsDriverless())
216 {
217 rc = VMMR3CallR0Emt(pVM, pVM->apCpusR3[0], VMMR0_DO_IOM_GROW_IO_PORT_STATS, cNewEntries, NULL);
218 AssertLogRelRCReturn(rc, rc);
219 AssertReturn(cNewEntries <= pVM->iom.s.cIoPortStatsAllocation, VERR_IOM_IOPORT_IPE_2);
220 }
221 else
222 {
223 /*
224 * Validate input and state.
225 */
226 uint32_t const cOldEntries = pVM->iom.s.cIoPortStatsAllocation;
227 AssertReturn(cNewEntries > cOldEntries, VERR_IOM_IOPORT_IPE_1);
228 AssertReturn(pVM->iom.s.cIoPortStats <= cOldEntries, VERR_IOM_IOPORT_IPE_2);
229
230 /*
231 * Calc size and allocate a new table.
232 */
233 uint32_t const cbNew = RT_ALIGN_32(cNewEntries * sizeof(IOMIOPORTSTATSENTRY), HOST_PAGE_SIZE);
234 cNewEntries = cbNew / sizeof(IOMIOPORTSTATSENTRY);
235
236 PIOMIOPORTSTATSENTRY const paIoPortStats = (PIOMIOPORTSTATSENTRY)RTMemPageAllocZ(cbNew);
237 if (paIoPortStats)
238 {
239 /*
240 * Anything to copy over, update and free the old one.
241 */
242 PIOMIOPORTSTATSENTRY const pOldIoPortStats = pVM->iom.s.paIoPortStats;
243 if (pOldIoPortStats)
244 memcpy(paIoPortStats, pOldIoPortStats, cOldEntries * sizeof(IOMIOPORTSTATSENTRY));
245
246 pVM->iom.s.paIoPortStats = paIoPortStats;
247 pVM->iom.s.cIoPortStatsAllocation = cNewEntries;
248
249 RTMemPageFree(pOldIoPortStats, RT_ALIGN_32(cOldEntries * sizeof(IOMIOPORTSTATSENTRY), HOST_PAGE_SIZE));
250
251 rc = VINF_SUCCESS;
252 }
253 else
254 rc = VERR_NO_PAGE_MEMORY;
255 }
256
257 return rc;
258}
259#endif
260
261
262/**
263 * Grows the I/O port registration statistics table.
264 *
265 * @returns VBox status code.
266 * @param pVM The cross context VM structure.
267 * @param cNewEntries The minimum number of new entrie.
268 * @see IOMR0IoPortGrowRegistrationTables
269 */
270static int iomR3IoPortGrowTable(PVM pVM, uint32_t cNewEntries)
271{
272 AssertReturn(cNewEntries <= _4K, VERR_IOM_TOO_MANY_IOPORT_REGISTRATIONS);
273
274 int rc;
275 if (!SUPR3IsDriverless())
276 {
277 rc = VMMR3CallR0Emt(pVM, pVM->apCpusR3[0], VMMR0_DO_IOM_GROW_IO_PORTS, cNewEntries, NULL);
278 AssertLogRelRCReturn(rc, rc);
279 AssertReturn(cNewEntries <= pVM->iom.s.cIoPortAlloc, VERR_IOM_IOPORT_IPE_2);
280 }
281 else
282 {
283 /*
284 * Validate input and state.
285 */
286 uint32_t const cOldEntries = pVM->iom.s.cIoPortAlloc;
287 AssertReturn(cNewEntries >= cOldEntries, VERR_IOM_IOPORT_IPE_1);
288
289 /*
290 * Allocate the new tables. We use a single allocation for the three tables (ring-0,
291 * ring-3, lookup) and does a partial mapping of the result to ring-3.
292 */
293 uint32_t const cbRing3 = RT_ALIGN_32(cNewEntries * sizeof(IOMIOPORTENTRYR3), HOST_PAGE_SIZE);
294 uint32_t const cbShared = RT_ALIGN_32(cNewEntries * sizeof(IOMIOPORTLOOKUPENTRY), HOST_PAGE_SIZE);
295 uint32_t const cbNew = cbRing3 + cbShared;
296
297 /* Use the rounded up space as best we can. */
298 cNewEntries = RT_MIN(cbRing3 / sizeof(IOMIOPORTENTRYR3), cbShared / sizeof(IOMIOPORTLOOKUPENTRY));
299
300 PIOMIOPORTENTRYR3 const paRing3 = (PIOMIOPORTENTRYR3)RTMemPageAllocZ(cbNew);
301 if (paRing3)
302 {
303 PIOMIOPORTLOOKUPENTRY const paLookup = (PIOMIOPORTLOOKUPENTRY)((uintptr_t)paRing3 + cbRing3);
304
305 /*
306 * Copy over the old info and initialize the idxSelf and idxStats members.
307 */
308 if (pVM->iom.s.paIoPortRegs != NULL)
309 {
310 memcpy(paRing3, pVM->iom.s.paIoPortRegs, sizeof(paRing3[0]) * cOldEntries);
311 memcpy(paLookup, pVM->iom.s.paIoPortLookup, sizeof(paLookup[0]) * cOldEntries);
312 }
313
314 size_t i = cbRing3 / sizeof(*paRing3);
315 while (i-- > cOldEntries)
316 {
317 paRing3[i].idxSelf = (uint16_t)i;
318 paRing3[i].idxStats = UINT16_MAX;
319 }
320
321 /*
322 * Update the variables and free the old memory.
323 */
324 void * const pvFree = pVM->iom.s.paIoPortRegs;
325
326 pVM->iom.s.paIoPortRegs = paRing3;
327 pVM->iom.s.paIoPortLookup = paLookup;
328 pVM->iom.s.cIoPortAlloc = cNewEntries;
329
330 RTMemPageFree(pvFree,
331 RT_ALIGN_32(cOldEntries * sizeof(IOMIOPORTENTRYR3), HOST_PAGE_SIZE)
332 + RT_ALIGN_32(cOldEntries * sizeof(IOMIOPORTLOOKUPENTRY), HOST_PAGE_SIZE));
333
334 rc = VINF_SUCCESS;
335 }
336 else
337 rc = VERR_NO_PAGE_MEMORY;
338 }
339 return rc;
340}
341
342
343/**
344 * Worker for PDMDEVHLPR3::pfnIoPortCreateEx.
345 */
346VMMR3_INT_DECL(int) IOMR3IoPortCreate(PVM pVM, PPDMDEVINS pDevIns, RTIOPORT cPorts, uint32_t fFlags, PPDMPCIDEV pPciDev,
347 uint32_t iPciRegion, PFNIOMIOPORTNEWOUT pfnOut, PFNIOMIOPORTNEWIN pfnIn,
348 PFNIOMIOPORTNEWOUTSTRING pfnOutStr, PFNIOMIOPORTNEWINSTRING pfnInStr, RTR3PTR pvUser,
349 const char *pszDesc, PCIOMIOPORTDESC paExtDescs, PIOMIOPORTHANDLE phIoPorts)
350{
351 /*
352 * Validate input.
353 */
354 AssertPtrReturn(phIoPorts, VERR_INVALID_POINTER);
355 *phIoPorts = UINT32_MAX;
356 VM_ASSERT_EMT0_RETURN(pVM, VERR_VM_THREAD_NOT_EMT);
357 VM_ASSERT_STATE_RETURN(pVM, VMSTATE_CREATING, VERR_VM_INVALID_VM_STATE);
358 AssertReturn(!pVM->iom.s.fIoPortsFrozen, VERR_WRONG_ORDER);
359
360 AssertPtrReturn(pDevIns, VERR_INVALID_POINTER);
361
362 AssertMsgReturn(cPorts > 0 && cPorts <= _8K, ("cPorts=%#x\n", cPorts), VERR_OUT_OF_RANGE);
363 AssertReturn(!(fFlags & ~IOM_IOPORT_F_VALID_MASK), VERR_INVALID_FLAGS);
364
365 AssertReturn(pfnOut || pfnIn || pfnOutStr || pfnInStr, VERR_INVALID_PARAMETER);
366 AssertPtrNullReturn(pfnOut, VERR_INVALID_POINTER);
367 AssertPtrNullReturn(pfnIn, VERR_INVALID_POINTER);
368 AssertPtrNullReturn(pfnOutStr, VERR_INVALID_POINTER);
369 AssertPtrNullReturn(pfnInStr, VERR_INVALID_POINTER);
370 AssertPtrReturn(pszDesc, VERR_INVALID_POINTER);
371 AssertReturn(*pszDesc != '\0', VERR_INVALID_POINTER);
372 AssertReturn(strlen(pszDesc) < 128, VERR_INVALID_POINTER);
373 if (paExtDescs)
374 {
375 AssertPtrReturn(paExtDescs, VERR_INVALID_POINTER);
376 for (size_t i = 0;; i++)
377 {
378 const char *pszIn = paExtDescs[i].pszIn;
379 const char *pszOut = paExtDescs[i].pszIn;
380 if (!pszIn && !pszOut)
381 break;
382 AssertReturn(i < _8K, VERR_OUT_OF_RANGE);
383 AssertReturn(!pszIn || strlen(pszIn) < 128, VERR_INVALID_POINTER);
384 AssertReturn(!pszOut || strlen(pszOut) < 128, VERR_INVALID_POINTER);
385 }
386 }
387
388 /*
389 * Ensure that we've got table space for it.
390 */
391#ifndef VBOX_WITH_STATISTICS
392 uint16_t const idxStats = UINT16_MAX;
393#else
394 uint32_t const idxStats = pVM->iom.s.cIoPortStats;
395 uint32_t const cNewIoPortStats = idxStats + cPorts;
396 AssertReturn(cNewIoPortStats <= _64K, VERR_IOM_TOO_MANY_IOPORT_REGISTRATIONS);
397 if (cNewIoPortStats > pVM->iom.s.cIoPortStatsAllocation)
398 {
399 int rc = iomR3IoPortGrowStatisticsTable(pVM, cNewIoPortStats);
400 AssertRCReturn(rc, rc);
401 AssertReturn(idxStats == pVM->iom.s.cIoPortStats, VERR_IOM_IOPORT_IPE_1);
402 }
403#endif
404
405 uint32_t idx = pVM->iom.s.cIoPortRegs;
406 if (idx >= pVM->iom.s.cIoPortAlloc)
407 {
408 int rc = iomR3IoPortGrowTable(pVM, pVM->iom.s.cIoPortAlloc + 1);
409 AssertRCReturn(rc, rc);
410 AssertReturn(idx == pVM->iom.s.cIoPortRegs, VERR_IOM_IOPORT_IPE_1);
411 AssertReturn(idx < pVM->iom.s.cIoPortAlloc, VERR_IOM_IOPORT_IPE_2);
412 }
413
414 /*
415 * Enter it.
416 */
417 pVM->iom.s.paIoPortRegs[idx].pvUser = pvUser;
418 pVM->iom.s.paIoPortRegs[idx].pDevIns = pDevIns;
419 pVM->iom.s.paIoPortRegs[idx].pfnOutCallback = pfnOut ? pfnOut : iomR3IOPortDummyNewOut;
420 pVM->iom.s.paIoPortRegs[idx].pfnInCallback = pfnIn ? pfnIn : iomR3IOPortDummyNewIn;
421 pVM->iom.s.paIoPortRegs[idx].pfnOutStrCallback = pfnOutStr ? pfnOutStr : iomR3IOPortDummyNewOutStr;
422 pVM->iom.s.paIoPortRegs[idx].pfnInStrCallback = pfnInStr ? pfnInStr : iomR3IOPortDummyNewInStr;
423 pVM->iom.s.paIoPortRegs[idx].pszDesc = pszDesc;
424 pVM->iom.s.paIoPortRegs[idx].paExtDescs = paExtDescs;
425 pVM->iom.s.paIoPortRegs[idx].pPciDev = pPciDev;
426 pVM->iom.s.paIoPortRegs[idx].iPciRegion = iPciRegion;
427 pVM->iom.s.paIoPortRegs[idx].cPorts = cPorts;
428 pVM->iom.s.paIoPortRegs[idx].uPort = UINT16_MAX;
429 pVM->iom.s.paIoPortRegs[idx].idxStats = (uint16_t)idxStats;
430 pVM->iom.s.paIoPortRegs[idx].fMapped = false;
431 pVM->iom.s.paIoPortRegs[idx].fFlags = (uint8_t)fFlags;
432 pVM->iom.s.paIoPortRegs[idx].idxSelf = idx;
433
434 pVM->iom.s.cIoPortRegs = idx + 1;
435#ifdef VBOX_WITH_STATISTICS
436 pVM->iom.s.cIoPortStats = cNewIoPortStats;
437#endif
438 *phIoPorts = idx;
439 LogFlow(("IOMR3IoPortCreate: idx=%#x cPorts=%u %s\n", idx, cPorts, pszDesc));
440 return VINF_SUCCESS;
441}
442
443
444/**
445 * Worker for PDMDEVHLPR3::pfnIoPortMap.
446 */
447VMMR3_INT_DECL(int) IOMR3IoPortMap(PVM pVM, PPDMDEVINS pDevIns, IOMIOPORTHANDLE hIoPorts, RTIOPORT uPort)
448{
449 /*
450 * Validate input and state.
451 */
452 AssertPtrReturn(pDevIns, VERR_INVALID_HANDLE);
453 AssertReturn(hIoPorts < pVM->iom.s.cIoPortRegs, VERR_IOM_INVALID_IOPORT_HANDLE);
454 PIOMIOPORTENTRYR3 const pRegEntry = &pVM->iom.s.paIoPortRegs[hIoPorts];
455 AssertReturn(pRegEntry->pDevIns == pDevIns, VERR_IOM_INVALID_IOPORT_HANDLE);
456
457 RTIOPORT const cPorts = pRegEntry->cPorts;
458 AssertMsgReturn(cPorts > 0 && cPorts <= _8K, ("cPorts=%s\n", cPorts), VERR_IOM_IOPORT_IPE_1);
459 AssertReturn((uint32_t)uPort + cPorts <= _64K, VERR_OUT_OF_RANGE);
460 RTIOPORT const uLastPort = uPort + cPorts - 1;
461 LogFlow(("IOMR3IoPortMap: hIoPorts=%#RX64 %RTiop..%RTiop (%u ports)\n", hIoPorts, uPort, uLastPort, cPorts));
462
463 /*
464 * Do the mapping.
465 */
466 int rc = VINF_SUCCESS;
467 IOM_LOCK_EXCL(pVM);
468
469 if (!pRegEntry->fMapped)
470 {
471 uint32_t const cEntries = RT_MIN(pVM->iom.s.cIoPortLookupEntries, pVM->iom.s.cIoPortRegs);
472 Assert(pVM->iom.s.cIoPortLookupEntries == cEntries);
473
474 PIOMIOPORTLOOKUPENTRY paEntries = pVM->iom.s.paIoPortLookup;
475 PIOMIOPORTLOOKUPENTRY pEntry;
476 if (cEntries > 0)
477 {
478 uint32_t iFirst = 0;
479 uint32_t iEnd = cEntries;
480 uint32_t i = cEntries / 2;
481 for (;;)
482 {
483 pEntry = &paEntries[i];
484 if (pEntry->uLastPort < uPort)
485 {
486 i += 1;
487 if (i < iEnd)
488 iFirst = i;
489 else
490 {
491 /* Insert after the entry we just considered: */
492 pEntry += 1;
493 if (i < cEntries)
494 memmove(pEntry + 1, pEntry, sizeof(*pEntry) * (cEntries - i));
495 break;
496 }
497 }
498 else if (pEntry->uFirstPort > uLastPort)
499 {
500 if (i > iFirst)
501 iEnd = i;
502 else
503 {
504 /* Insert at the entry we just considered: */
505 if (i < cEntries)
506 memmove(pEntry + 1, pEntry, sizeof(*pEntry) * (cEntries - i));
507 break;
508 }
509 }
510 else
511 {
512 /* Oops! We've got a conflict. */
513 AssertLogRelMsgFailed(("%x..%x (%s) conflicts with existing mapping %x..%x (%s)\n",
514 uPort, uLastPort, pRegEntry->pszDesc,
515 pEntry->uFirstPort, pEntry->uLastPort, pVM->iom.s.paIoPortRegs[pEntry->idx].pszDesc));
516 IOM_UNLOCK_EXCL(pVM);
517 return VERR_IOM_IOPORT_RANGE_CONFLICT;
518 }
519
520 i = iFirst + (iEnd - iFirst) / 2;
521 }
522 }
523 else
524 pEntry = paEntries;
525
526 /*
527 * Fill in the entry and bump the table size.
528 */
529 pEntry->idx = hIoPorts;
530 pEntry->uFirstPort = uPort;
531 pEntry->uLastPort = uLastPort;
532 pVM->iom.s.cIoPortLookupEntries = cEntries + 1;
533
534 pRegEntry->uPort = uPort;
535 pRegEntry->fMapped = true;
536
537#ifdef VBOX_WITH_STATISTICS
538 /* Don't register stats here when we're creating the VM as the
539 statistics table may still be reallocated. */
540 if (pVM->enmVMState >= VMSTATE_CREATED)
541 iomR3IoPortRegStats(pVM, pRegEntry);
542#endif
543
544#ifdef VBOX_STRICT
545 /*
546 * Assert table sanity.
547 */
548 AssertMsg(paEntries[0].uLastPort >= paEntries[0].uFirstPort, ("%#x %#x\n", paEntries[0].uLastPort, paEntries[0].uFirstPort));
549 AssertMsg(paEntries[0].idx < pVM->iom.s.cIoPortRegs, ("%#x %#x\n", paEntries[0].idx, pVM->iom.s.cIoPortRegs));
550
551 RTIOPORT uPortPrev = paEntries[0].uLastPort;
552 for (size_t i = 1; i <= cEntries; i++)
553 {
554 AssertMsg(paEntries[i].uLastPort >= paEntries[i].uFirstPort, ("%u: %#x %#x\n", i, paEntries[i].uLastPort, paEntries[i].uFirstPort));
555 AssertMsg(paEntries[i].idx < pVM->iom.s.cIoPortRegs, ("%u: %#x %#x\n", i, paEntries[i].idx, pVM->iom.s.cIoPortRegs));
556 AssertMsg(uPortPrev < paEntries[i].uFirstPort, ("%u: %#x %#x\n", i, uPortPrev, paEntries[i].uFirstPort));
557 AssertMsg(paEntries[i].uLastPort - paEntries[i].uFirstPort + 1 == pVM->iom.s.paIoPortRegs[paEntries[i].idx].cPorts,
558 ("%u: %#x %#x..%#x -> %u, expected %u\n", i, uPortPrev, paEntries[i].uFirstPort, paEntries[i].uLastPort,
559 paEntries[i].uLastPort - paEntries[i].uFirstPort + 1, pVM->iom.s.paIoPortRegs[paEntries[i].idx].cPorts));
560 uPortPrev = paEntries[i].uLastPort;
561 }
562#endif
563 }
564 else
565 {
566 AssertFailed();
567 rc = VERR_IOM_IOPORTS_ALREADY_MAPPED;
568 }
569
570 IOM_UNLOCK_EXCL(pVM);
571 return rc;
572}
573
574
575/**
576 * Worker for PDMDEVHLPR3::pfnIoPortUnmap.
577 */
578VMMR3_INT_DECL(int) IOMR3IoPortUnmap(PVM pVM, PPDMDEVINS pDevIns, IOMIOPORTHANDLE hIoPorts)
579{
580 /*
581 * Validate input and state.
582 */
583 AssertPtrReturn(pDevIns, VERR_INVALID_HANDLE);
584 AssertReturn(hIoPorts < pVM->iom.s.cIoPortRegs, VERR_IOM_INVALID_IOPORT_HANDLE);
585 PIOMIOPORTENTRYR3 const pRegEntry = &pVM->iom.s.paIoPortRegs[hIoPorts];
586 AssertReturn(pRegEntry->pDevIns == pDevIns, VERR_IOM_INVALID_IOPORT_HANDLE);
587
588 /*
589 * Do the mapping.
590 */
591 int rc;
592 IOM_LOCK_EXCL(pVM);
593
594 if (pRegEntry->fMapped)
595 {
596 RTIOPORT const uPort = pRegEntry->uPort;
597 RTIOPORT const uLastPort = uPort + pRegEntry->cPorts - 1;
598 uint32_t const cEntries = RT_MIN(pVM->iom.s.cIoPortLookupEntries, pVM->iom.s.cIoPortRegs);
599 Assert(pVM->iom.s.cIoPortLookupEntries == cEntries);
600 Assert(cEntries > 0);
601 LogFlow(("IOMR3IoPortUnmap: hIoPorts=%#RX64 %RTiop..%RTiop (%u ports)\n", hIoPorts, uPort, uLastPort, pRegEntry->cPorts));
602
603 PIOMIOPORTLOOKUPENTRY paEntries = pVM->iom.s.paIoPortLookup;
604 uint32_t iFirst = 0;
605 uint32_t iEnd = cEntries;
606 uint32_t i = cEntries / 2;
607 for (;;)
608 {
609 PIOMIOPORTLOOKUPENTRY pEntry = &paEntries[i];
610 if (pEntry->uLastPort < uPort)
611 {
612 i += 1;
613 if (i < iEnd)
614 iFirst = i;
615 else
616 {
617 rc = VERR_IOM_IOPORT_IPE_1;
618 AssertLogRelMsgFailedBreak(("%x..%x (%s) not found!\n", uPort, uLastPort, pRegEntry->pszDesc));
619 }
620 }
621 else if (pEntry->uFirstPort > uLastPort)
622 {
623 if (i > iFirst)
624 iEnd = i;
625 else
626 {
627 rc = VERR_IOM_IOPORT_IPE_1;
628 AssertLogRelMsgFailedBreak(("%x..%x (%s) not found!\n", uPort, uLastPort, pRegEntry->pszDesc));
629 }
630 }
631 else if (pEntry->idx == hIoPorts)
632 {
633 Assert(pEntry->uFirstPort == uPort);
634 Assert(pEntry->uLastPort == uLastPort);
635#ifdef VBOX_WITH_STATISTICS
636 iomR3IoPortDeregStats(pVM, pRegEntry, uPort);
637#endif
638 if (i + 1 < cEntries)
639 memmove(pEntry, pEntry + 1, sizeof(*pEntry) * (cEntries - i - 1));
640 pVM->iom.s.cIoPortLookupEntries = cEntries - 1;
641 pRegEntry->uPort = UINT16_MAX;
642 pRegEntry->fMapped = false;
643 rc = VINF_SUCCESS;
644 break;
645 }
646 else
647 {
648 AssertLogRelMsgFailed(("Lookig for %x..%x (%s), found %x..%x (%s) instead!\n",
649 uPort, uLastPort, pRegEntry->pszDesc,
650 pEntry->uFirstPort, pEntry->uLastPort, pVM->iom.s.paIoPortRegs[pEntry->idx].pszDesc));
651 rc = VERR_IOM_IOPORT_IPE_1;
652 break;
653 }
654
655 i = iFirst + (iEnd - iFirst) / 2;
656 }
657
658#ifdef VBOX_STRICT
659 /*
660 * Assert table sanity.
661 */
662 AssertMsg(paEntries[0].uLastPort >= paEntries[0].uFirstPort, ("%#x %#x\n", paEntries[0].uLastPort, paEntries[0].uFirstPort));
663 AssertMsg(paEntries[0].idx < pVM->iom.s.cIoPortRegs, ("%#x %#x\n", paEntries[0].idx, pVM->iom.s.cIoPortRegs));
664
665 RTIOPORT uPortPrev = paEntries[0].uLastPort;
666 for (i = 1; i < cEntries - 1; i++)
667 {
668 AssertMsg(paEntries[i].uLastPort >= paEntries[i].uFirstPort, ("%u: %#x %#x\n", i, paEntries[i].uLastPort, paEntries[i].uFirstPort));
669 AssertMsg(paEntries[i].idx < pVM->iom.s.cIoPortRegs, ("%u: %#x %#x\n", i, paEntries[i].idx, pVM->iom.s.cIoPortRegs));
670 AssertMsg(uPortPrev < paEntries[i].uFirstPort, ("%u: %#x %#x\n", i, uPortPrev, paEntries[i].uFirstPort));
671 AssertMsg(paEntries[i].uLastPort - paEntries[i].uFirstPort + 1 == pVM->iom.s.paIoPortRegs[paEntries[i].idx].cPorts,
672 ("%u: %#x %#x..%#x -> %u, expected %u\n", i, uPortPrev, paEntries[i].uFirstPort, paEntries[i].uLastPort,
673 paEntries[i].uLastPort - paEntries[i].uFirstPort + 1, pVM->iom.s.paIoPortRegs[paEntries[i].idx].cPorts));
674 uPortPrev = paEntries[i].uLastPort;
675 }
676#endif
677 }
678 else
679 {
680 AssertFailed();
681 rc = VERR_IOM_IOPORTS_NOT_MAPPED;
682 }
683
684 IOM_UNLOCK_EXCL(pVM);
685 return rc;
686}
687
688
689/**
690 * Validates @a hIoPorts, making sure it belongs to @a pDevIns.
691 *
692 * @returns VBox status code.
693 * @param pVM The cross context VM structure.
694 * @param pDevIns The device which allegedly owns @a hIoPorts.
695 * @param hIoPorts The handle to validate.
696 */
697VMMR3_INT_DECL(int) IOMR3IoPortValidateHandle(PVM pVM, PPDMDEVINS pDevIns, IOMIOPORTHANDLE hIoPorts)
698{
699 AssertPtrReturn(pDevIns, VERR_INVALID_HANDLE);
700 AssertReturn(hIoPorts < RT_MIN(pVM->iom.s.cIoPortRegs, pVM->iom.s.cIoPortAlloc), VERR_IOM_INVALID_IOPORT_HANDLE);
701 PIOMIOPORTENTRYR3 const pRegEntry = &pVM->iom.s.paIoPortRegs[hIoPorts];
702 AssertReturn(pRegEntry->pDevIns == pDevIns, VERR_IOM_INVALID_IOPORT_HANDLE);
703 return VINF_SUCCESS;
704}
705
706
707/**
708 * Gets the mapping address of I/O ports @a hIoPorts.
709 *
710 * @returns Mapping address if mapped, UINT32_MAX if not mapped or invalid
711 * input.
712 * @param pVM The cross context VM structure.
713 * @param pDevIns The device which allegedly owns @a hRegion.
714 * @param hIoPorts The handle to I/O port region.
715 */
716VMMR3_INT_DECL(uint32_t) IOMR3IoPortGetMappingAddress(PVM pVM, PPDMDEVINS pDevIns, IOMIOPORTHANDLE hIoPorts)
717{
718 AssertPtrReturn(pDevIns, UINT32_MAX);
719 AssertReturn(hIoPorts < RT_MIN(pVM->iom.s.cIoPortRegs, pVM->iom.s.cIoPortAlloc), UINT32_MAX);
720 IOMIOPORTENTRYR3 volatile * const pRegEntry = &pVM->iom.s.paIoPortRegs[hIoPorts];
721 AssertReturn(pRegEntry->pDevIns == pDevIns, UINT32_MAX);
722 for (uint32_t iTry = 0; ; iTry++)
723 {
724 bool fMapped = pRegEntry->fMapped;
725 RTIOPORT uPort = pRegEntry->uPort;
726 if ( ( ASMAtomicReadBool(&pRegEntry->fMapped) == fMapped
727 && uPort == pRegEntry->uPort)
728 || iTry > 1024)
729 return fMapped ? uPort : UINT32_MAX;
730 ASMNopPause();
731 }
732}
733
734
735/**
736 * Display all registered I/O port ranges.
737 *
738 * @param pVM The cross context VM structure.
739 * @param pHlp The info helpers.
740 * @param pszArgs Arguments, ignored.
741 */
742DECLCALLBACK(void) iomR3IoPortInfo(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs)
743{
744 RT_NOREF(pszArgs);
745
746 /* No locking needed here as registerations are only happening during VMSTATE_CREATING. */
747 pHlp->pfnPrintf(pHlp,
748 "I/O port registrations: %u (%u allocated)\n"
749 " ## Ctx Ports Mapping PCI Description\n",
750 pVM->iom.s.cIoPortRegs, pVM->iom.s.cIoPortAlloc);
751 PIOMIOPORTENTRYR3 paRegs = pVM->iom.s.paIoPortRegs;
752 for (uint32_t i = 0; i < pVM->iom.s.cIoPortRegs; i++)
753 {
754 const char * const pszRing = paRegs[i].fRing0 ? paRegs[i].fRawMode ? "+0+C" : "+0 "
755 : paRegs[i].fRawMode ? "+C " : " ";
756 if (paRegs[i].fMapped && paRegs[i].pPciDev)
757 pHlp->pfnPrintf(pHlp, "%3u R3%s %04x %04x-%04x pci%u/%u %s\n", paRegs[i].idxSelf, pszRing, paRegs[i].cPorts,
758 paRegs[i].uPort, paRegs[i].uPort + paRegs[i].cPorts - 1,
759 paRegs[i].pPciDev->idxSubDev, paRegs[i].iPciRegion, paRegs[i].pszDesc);
760 else if (paRegs[i].fMapped && !paRegs[i].pPciDev)
761 pHlp->pfnPrintf(pHlp, "%3u R3%s %04x %04x-%04x %s\n", paRegs[i].idxSelf, pszRing, paRegs[i].cPorts,
762 paRegs[i].uPort, paRegs[i].uPort + paRegs[i].cPorts - 1, paRegs[i].pszDesc);
763 else if (paRegs[i].pPciDev)
764 pHlp->pfnPrintf(pHlp, "%3u R3%s %04x unmapped pci%u/%u %s\n", paRegs[i].idxSelf, pszRing, paRegs[i].cPorts,
765 paRegs[i].pPciDev->idxSubDev, paRegs[i].iPciRegion, paRegs[i].pszDesc);
766 else
767 pHlp->pfnPrintf(pHlp, "%3u R3%s %04x unmapped %s\n",
768 paRegs[i].idxSelf, pszRing, paRegs[i].cPorts, paRegs[i].pszDesc);
769 }
770}
771
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