VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/IOMAll.cpp@ 73299

Last change on this file since 73299 was 72493, checked in by vboxsync, 7 years ago

IEM,REM,++: Removed code related IEM_VERIFICATION_MODE and friends because it (1) adds aditional complexity and mess, (2) suffers bit rot as it's infrequently used, and (3) prevents using pVCpu->cpum.GstCtx directly.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 27.0 KB
Line 
1/* $Id: IOMAll.cpp 72493 2018-06-10 16:08:44Z vboxsync $ */
2/** @file
3 * IOM - Input / Output Monitor - Any Context.
4 */
5
6/*
7 * Copyright (C) 2006-2017 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 <VBox/vmm/mm.h>
25#include <VBox/param.h>
26#include "IOMInternal.h"
27#include <VBox/vmm/vm.h>
28#include <VBox/vmm/vmm.h>
29#include <VBox/vmm/selm.h>
30#include <VBox/vmm/trpm.h>
31#include <VBox/vmm/pdmdev.h>
32#include <VBox/vmm/pgm.h>
33#include <VBox/vmm/cpum.h>
34#include <VBox/err.h>
35#include <VBox/log.h>
36#include <iprt/assert.h>
37#include "IOMInline.h"
38
39
40/**
41 * Check if this VCPU currently owns the IOM lock exclusively.
42 *
43 * @returns bool owner/not owner
44 * @param pVM The cross context VM structure.
45 */
46VMMDECL(bool) IOMIsLockWriteOwner(PVM pVM)
47{
48#ifdef IOM_WITH_CRIT_SECT_RW
49 return PDMCritSectRwIsInitialized(&pVM->iom.s.CritSect)
50 && PDMCritSectRwIsWriteOwner(&pVM->iom.s.CritSect);
51#else
52 return PDMCritSectIsOwner(&pVM->iom.s.CritSect);
53#endif
54}
55
56
57//#undef LOG_GROUP
58//#define LOG_GROUP LOG_GROUP_IOM_IOPORT
59
60/**
61 * Reads an I/O port register.
62 *
63 * @returns Strict VBox status code. Informational status codes other than the one documented
64 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
65 * @retval VINF_SUCCESS Success.
66 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
67 * status code must be passed on to EM.
68 * @retval VINF_IOM_R3_IOPORT_READ Defer the read to ring-3. (R0/RC only)
69 *
70 * @param pVM The cross context VM structure.
71 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
72 * @param Port The port to read.
73 * @param pu32Value Where to store the value read.
74 * @param cbValue The size of the register to read in bytes. 1, 2 or 4 bytes.
75 */
76VMMDECL(VBOXSTRICTRC) IOMIOPortRead(PVM pVM, PVMCPU pVCpu, RTIOPORT Port, uint32_t *pu32Value, size_t cbValue)
77{
78 Assert(pVCpu->iom.s.PendingIOPortWrite.cbValue == 0);
79
80/** @todo should initialize *pu32Value here because it can happen that some
81 * handle is buggy and doesn't handle all cases. */
82 /* Take the IOM lock before performing any device I/O. */
83 int rc2 = IOM_LOCK_SHARED(pVM);
84#ifndef IN_RING3
85 if (rc2 == VERR_SEM_BUSY)
86 return VINF_IOM_R3_IOPORT_READ;
87#endif
88 AssertRC(rc2);
89
90#ifdef VBOX_WITH_STATISTICS
91 /*
92 * Get the statistics record.
93 */
94 PIOMIOPORTSTATS pStats = pVCpu->iom.s.CTX_SUFF(pStatsLastRead);
95 if (!pStats || pStats->Core.Key != Port)
96 {
97 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTX_SUFF(pTrees)->IOPortStatTree, Port);
98 if (pStats)
99 pVCpu->iom.s.CTX_SUFF(pStatsLastRead) = pStats;
100 }
101#endif
102
103 /*
104 * Get handler for current context.
105 */
106 CTX_SUFF(PIOMIOPORTRANGE) pRange = pVCpu->iom.s.CTX_SUFF(pRangeLastRead);
107 if ( !pRange
108 || (unsigned)Port - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
109 {
110 pRange = iomIOPortGetRange(pVM, Port);
111 if (pRange)
112 pVCpu->iom.s.CTX_SUFF(pRangeLastRead) = pRange;
113 }
114 MMHYPER_RC_ASSERT_RCPTR(pVM, pRange);
115 if (pRange)
116 {
117 /*
118 * Found a range, get the data in case we leave the IOM lock.
119 */
120 PFNIOMIOPORTIN pfnInCallback = pRange->pfnInCallback;
121#ifndef IN_RING3
122 if (pfnInCallback)
123 { /* likely */ }
124 else
125 {
126 STAM_STATS({ if (pStats) STAM_COUNTER_INC(&pStats->InRZToR3); });
127 IOM_UNLOCK_SHARED(pVM);
128 return VINF_IOM_R3_IOPORT_READ;
129 }
130#endif
131 void *pvUser = pRange->pvUser;
132 PPDMDEVINS pDevIns = pRange->pDevIns;
133 IOM_UNLOCK_SHARED(pVM);
134
135 /*
136 * Call the device.
137 */
138 VBOXSTRICTRC rcStrict = PDMCritSectEnter(pDevIns->CTX_SUFF(pCritSectRo), VINF_IOM_R3_IOPORT_READ);
139 if (rcStrict == VINF_SUCCESS)
140 { /* likely */ }
141 else
142 {
143 STAM_STATS({ if (pStats) STAM_COUNTER_INC(&pStats->InRZToR3); });
144 return rcStrict;
145 }
146#ifdef VBOX_WITH_STATISTICS
147 if (pStats)
148 {
149 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfIn), a);
150 rcStrict = pfnInCallback(pDevIns, pvUser, Port, pu32Value, (unsigned)cbValue);
151 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfIn), a);
152 }
153 else
154#endif
155 rcStrict = pfnInCallback(pDevIns, pvUser, Port, pu32Value, (unsigned)cbValue);
156 PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo));
157
158#ifdef VBOX_WITH_STATISTICS
159 if (rcStrict == VINF_SUCCESS && pStats)
160 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(In));
161# ifndef IN_RING3
162 else if (rcStrict == VINF_IOM_R3_IOPORT_READ && pStats)
163 STAM_COUNTER_INC(&pStats->InRZToR3);
164# endif
165#endif
166 if (rcStrict == VERR_IOM_IOPORT_UNUSED)
167 {
168 /* make return value */
169 rcStrict = VINF_SUCCESS;
170 switch (cbValue)
171 {
172 case 1: *(uint8_t *)pu32Value = 0xff; break;
173 case 2: *(uint16_t *)pu32Value = 0xffff; break;
174 case 4: *(uint32_t *)pu32Value = UINT32_C(0xffffffff); break;
175 default:
176 AssertMsgFailed(("Invalid I/O port size %d. Port=%d\n", cbValue, Port));
177 return VERR_IOM_INVALID_IOPORT_SIZE;
178 }
179 }
180 Log3(("IOMIOPortRead: Port=%RTiop *pu32=%08RX32 cb=%d rc=%Rrc\n", Port, *pu32Value, cbValue, VBOXSTRICTRC_VAL(rcStrict)));
181 return rcStrict;
182 }
183
184#ifndef IN_RING3
185 /*
186 * Handler in ring-3?
187 */
188 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeR3(pVM, Port);
189 if (pRangeR3)
190 {
191# ifdef VBOX_WITH_STATISTICS
192 if (pStats)
193 STAM_COUNTER_INC(&pStats->InRZToR3);
194# endif
195 IOM_UNLOCK_SHARED(pVM);
196 return VINF_IOM_R3_IOPORT_READ;
197 }
198#endif
199
200 /*
201 * Ok, no handler for this port.
202 */
203#ifdef VBOX_WITH_STATISTICS
204 if (pStats)
205 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(In));
206#endif
207
208 /* make return value */
209 switch (cbValue)
210 {
211 case 1: *(uint8_t *)pu32Value = 0xff; break;
212 case 2: *(uint16_t *)pu32Value = 0xffff; break;
213 case 4: *(uint32_t *)pu32Value = UINT32_C(0xffffffff); break;
214 default:
215 AssertMsgFailed(("Invalid I/O port size %d. Port=%d\n", cbValue, Port));
216 IOM_UNLOCK_SHARED(pVM);
217 return VERR_IOM_INVALID_IOPORT_SIZE;
218 }
219 Log3(("IOMIOPortRead: Port=%RTiop *pu32=%08RX32 cb=%d rc=VINF_SUCCESS\n", Port, *pu32Value, cbValue));
220 IOM_UNLOCK_SHARED(pVM);
221 return VINF_SUCCESS;
222}
223
224
225/**
226 * Reads the string buffer of an I/O port register.
227 *
228 * @returns Strict VBox status code. Informational status codes other than the one documented
229 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
230 * @retval VINF_SUCCESS Success or no string I/O callback in
231 * this context.
232 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
233 * status code must be passed on to EM.
234 * @retval VINF_IOM_R3_IOPORT_READ Defer the read to ring-3. (R0/RC only)
235 *
236 * @param pVM The cross context VM structure.
237 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
238 * @param uPort The port to read.
239 * @param pvDst Pointer to the destination buffer.
240 * @param pcTransfers Pointer to the number of transfer units to read, on return remaining transfer units.
241 * @param cb Size of the transfer unit (1, 2 or 4 bytes).
242 */
243VMM_INT_DECL(VBOXSTRICTRC) IOMIOPortReadString(PVM pVM, PVMCPU pVCpu, RTIOPORT uPort,
244 void *pvDst, uint32_t *pcTransfers, unsigned cb)
245{
246 Assert(pVCpu->iom.s.PendingIOPortWrite.cbValue == 0);
247
248 /* Take the IOM lock before performing any device I/O. */
249 int rc2 = IOM_LOCK_SHARED(pVM);
250#ifndef IN_RING3
251 if (rc2 == VERR_SEM_BUSY)
252 return VINF_IOM_R3_IOPORT_READ;
253#endif
254 AssertRC(rc2);
255
256 const uint32_t cRequestedTransfers = *pcTransfers;
257 Assert(cRequestedTransfers > 0);
258
259#ifdef VBOX_WITH_STATISTICS
260 /*
261 * Get the statistics record.
262 */
263 PIOMIOPORTSTATS pStats = pVCpu->iom.s.CTX_SUFF(pStatsLastRead);
264 if (!pStats || pStats->Core.Key != uPort)
265 {
266 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTX_SUFF(pTrees)->IOPortStatTree, uPort);
267 if (pStats)
268 pVCpu->iom.s.CTX_SUFF(pStatsLastRead) = pStats;
269 }
270#endif
271
272 /*
273 * Get handler for current context.
274 */
275 CTX_SUFF(PIOMIOPORTRANGE) pRange = pVCpu->iom.s.CTX_SUFF(pRangeLastRead);
276 if ( !pRange
277 || (unsigned)uPort - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
278 {
279 pRange = iomIOPortGetRange(pVM, uPort);
280 if (pRange)
281 pVCpu->iom.s.CTX_SUFF(pRangeLastRead) = pRange;
282 }
283 MMHYPER_RC_ASSERT_RCPTR(pVM, pRange);
284 if (pRange)
285 {
286 /*
287 * Found a range.
288 */
289 PFNIOMIOPORTINSTRING pfnInStrCallback = pRange->pfnInStrCallback;
290 PFNIOMIOPORTIN pfnInCallback = pRange->pfnInCallback;
291#ifndef IN_RING3
292 if (pfnInStrCallback || pfnInCallback)
293 { /* likely */ }
294 else
295 {
296 STAM_STATS({ if (pStats) STAM_COUNTER_INC(&pStats->InRZToR3); });
297 IOM_UNLOCK_SHARED(pVM);
298 return VINF_IOM_R3_IOPORT_READ;
299 }
300#endif
301 void *pvUser = pRange->pvUser;
302 PPDMDEVINS pDevIns = pRange->pDevIns;
303 IOM_UNLOCK_SHARED(pVM);
304
305 /*
306 * Call the device.
307 */
308 VBOXSTRICTRC rcStrict = PDMCritSectEnter(pDevIns->CTX_SUFF(pCritSectRo), VINF_IOM_R3_IOPORT_READ);
309 if (rcStrict == VINF_SUCCESS)
310 { /* likely */ }
311 else
312 {
313 STAM_STATS({ if (pStats) STAM_COUNTER_INC(&pStats->InRZToR3); });
314 return rcStrict;
315 }
316
317 /*
318 * First using the string I/O callback.
319 */
320 if (pfnInStrCallback)
321 {
322#ifdef VBOX_WITH_STATISTICS
323 if (pStats)
324 {
325 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfIn), a);
326 rcStrict = pfnInStrCallback(pDevIns, pvUser, uPort, (uint8_t *)pvDst, pcTransfers, cb);
327 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfIn), a);
328 }
329 else
330#endif
331 rcStrict = pfnInStrCallback(pDevIns, pvUser, uPort, (uint8_t *)pvDst, pcTransfers, cb);
332 }
333
334 /*
335 * Then doing the single I/O fallback.
336 */
337 if ( *pcTransfers > 0
338 && rcStrict == VINF_SUCCESS)
339 {
340 pvDst = (uint8_t *)pvDst + (cRequestedTransfers - *pcTransfers) * cb;
341 do
342 {
343 uint32_t u32Value = 0;
344#ifdef VBOX_WITH_STATISTICS
345 if (pStats)
346 {
347 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfIn), a);
348 rcStrict = pfnInCallback(pDevIns, pvUser, uPort, &u32Value, cb);
349 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfIn), a);
350 }
351 else
352#endif
353 rcStrict = pfnInCallback(pDevIns, pvUser, uPort, &u32Value, cb);
354 if (rcStrict == VERR_IOM_IOPORT_UNUSED)
355 {
356 u32Value = UINT32_MAX;
357 rcStrict = VINF_SUCCESS;
358 }
359 if (IOM_SUCCESS(rcStrict))
360 {
361 switch (cb)
362 {
363 case 4: *(uint32_t *)pvDst = u32Value; pvDst = (uint8_t *)pvDst + 4; break;
364 case 2: *(uint16_t *)pvDst = (uint16_t)u32Value; pvDst = (uint8_t *)pvDst + 2; break;
365 case 1: *(uint8_t *)pvDst = (uint8_t )u32Value; pvDst = (uint8_t *)pvDst + 1; break;
366 default: AssertFailed();
367 }
368 *pcTransfers -= 1;
369 }
370 } while ( *pcTransfers > 0
371 && rcStrict == VINF_SUCCESS);
372 }
373 PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo));
374
375#ifdef VBOX_WITH_STATISTICS
376 if (rcStrict == VINF_SUCCESS && pStats)
377 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(In));
378# ifndef IN_RING3
379 else if (rcStrict == VINF_IOM_R3_IOPORT_READ && pStats)
380 STAM_COUNTER_INC(&pStats->InRZToR3);
381# endif
382#endif
383 Log3(("IOMIOPortReadStr: uPort=%RTiop pvDst=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=%Rrc\n",
384 uPort, pvDst, pcTransfers, cRequestedTransfers, *pcTransfers, cb, VBOXSTRICTRC_VAL(rcStrict)));
385 return rcStrict;
386 }
387
388#ifndef IN_RING3
389 /*
390 * Handler in ring-3?
391 */
392 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeR3(pVM, uPort);
393 if (pRangeR3)
394 {
395# ifdef VBOX_WITH_STATISTICS
396 if (pStats)
397 STAM_COUNTER_INC(&pStats->InRZToR3);
398# endif
399 IOM_UNLOCK_SHARED(pVM);
400 return VINF_IOM_R3_IOPORT_READ;
401 }
402#endif
403
404 /*
405 * Ok, no handler for this port.
406 */
407 *pcTransfers = 0;
408 memset(pvDst, 0xff, cRequestedTransfers * cb);
409#ifdef VBOX_WITH_STATISTICS
410 if (pStats)
411 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(In));
412#endif
413 Log3(("IOMIOPortReadStr: uPort=%RTiop (unused) pvDst=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=VINF_SUCCESS\n",
414 uPort, pvDst, pcTransfers, cRequestedTransfers, *pcTransfers, cb));
415 IOM_UNLOCK_SHARED(pVM);
416 return VINF_SUCCESS;
417}
418
419
420#ifndef IN_RING3
421/**
422 * Defers a pending I/O port write to ring-3.
423 *
424 * @returns VINF_IOM_R3_IOPORT_COMMIT_WRITE
425 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
426 * @param Port The port to write to.
427 * @param u32Value The value to write.
428 * @param cbValue The size of the value (1, 2, 4).
429 */
430static VBOXSTRICTRC iomIOPortRing3WritePending(PVMCPU pVCpu, RTIOPORT Port, uint32_t u32Value, size_t cbValue)
431{
432 Log5(("iomIOPortRing3WritePending: %#x LB %u -> %RTiop\n", u32Value, cbValue, Port));
433 AssertReturn(pVCpu->iom.s.PendingIOPortWrite.cbValue == 0, VERR_IOM_IOPORT_IPE_1);
434 pVCpu->iom.s.PendingIOPortWrite.IOPort = Port;
435 pVCpu->iom.s.PendingIOPortWrite.u32Value = u32Value;
436 pVCpu->iom.s.PendingIOPortWrite.cbValue = (uint32_t)cbValue;
437 VMCPU_FF_SET(pVCpu, VMCPU_FF_IOM);
438 return VINF_IOM_R3_IOPORT_COMMIT_WRITE;
439}
440#endif
441
442
443/**
444 * Writes to an I/O port register.
445 *
446 * @returns Strict VBox status code. Informational status codes other than the one documented
447 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
448 * @retval VINF_SUCCESS Success.
449 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
450 * status code must be passed on to EM.
451 * @retval VINF_IOM_R3_IOPORT_WRITE Defer the write to ring-3. (R0/RC only)
452 *
453 * @param pVM The cross context VM structure.
454 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
455 * @param Port The port to write to.
456 * @param u32Value The value to write.
457 * @param cbValue The size of the register to read in bytes. 1, 2 or 4 bytes.
458 */
459VMMDECL(VBOXSTRICTRC) IOMIOPortWrite(PVM pVM, PVMCPU pVCpu, RTIOPORT Port, uint32_t u32Value, size_t cbValue)
460{
461#ifndef IN_RING3
462 Assert(pVCpu->iom.s.PendingIOPortWrite.cbValue == 0);
463#endif
464
465 /* Take the IOM lock before performing any device I/O. */
466 int rc2 = IOM_LOCK_SHARED(pVM);
467#ifndef IN_RING3
468 if (rc2 == VERR_SEM_BUSY)
469 return iomIOPortRing3WritePending(pVCpu, Port, u32Value, cbValue);
470#endif
471 AssertRC(rc2);
472
473/** @todo bird: When I get time, I'll remove the RC/R0 trees and link the RC/R0
474 * entries to the ring-3 node. */
475#ifdef VBOX_WITH_STATISTICS
476 /*
477 * Find the statistics record.
478 */
479 PIOMIOPORTSTATS pStats = pVCpu->iom.s.CTX_SUFF(pStatsLastWrite);
480 if (!pStats || pStats->Core.Key != Port)
481 {
482 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTX_SUFF(pTrees)->IOPortStatTree, Port);
483 if (pStats)
484 pVCpu->iom.s.CTX_SUFF(pStatsLastWrite) = pStats;
485 }
486#endif
487
488 /*
489 * Get handler for current context.
490 */
491 CTX_SUFF(PIOMIOPORTRANGE) pRange = pVCpu->iom.s.CTX_SUFF(pRangeLastWrite);
492 if ( !pRange
493 || (unsigned)Port - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
494 {
495 pRange = iomIOPortGetRange(pVM, Port);
496 if (pRange)
497 pVCpu->iom.s.CTX_SUFF(pRangeLastWrite) = pRange;
498 }
499 MMHYPER_RC_ASSERT_RCPTR(pVM, pRange);
500 if (pRange)
501 {
502 /*
503 * Found a range.
504 */
505 PFNIOMIOPORTOUT pfnOutCallback = pRange->pfnOutCallback;
506#ifndef IN_RING3
507 if (pfnOutCallback)
508 { /* likely */ }
509 else
510 {
511 STAM_STATS({ if (pStats) STAM_COUNTER_INC(&pStats->OutRZToR3); });
512 IOM_UNLOCK_SHARED(pVM);
513 return iomIOPortRing3WritePending(pVCpu, Port, u32Value, cbValue);
514 }
515#endif
516 void *pvUser = pRange->pvUser;
517 PPDMDEVINS pDevIns = pRange->pDevIns;
518 IOM_UNLOCK_SHARED(pVM);
519
520 /*
521 * Call the device.
522 */
523 VBOXSTRICTRC rcStrict = PDMCritSectEnter(pDevIns->CTX_SUFF(pCritSectRo), VINF_IOM_R3_IOPORT_WRITE);
524 if (rcStrict == VINF_SUCCESS)
525 { /* likely */ }
526 else
527 {
528 STAM_STATS({ if (pStats) STAM_COUNTER_INC(&pStats->OutRZToR3); });
529#ifndef IN_RING3
530 if (RT_LIKELY(rcStrict == VINF_IOM_R3_IOPORT_WRITE))
531 return iomIOPortRing3WritePending(pVCpu, Port, u32Value, cbValue);
532#endif
533 return rcStrict;
534 }
535#ifdef VBOX_WITH_STATISTICS
536 if (pStats)
537 {
538 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfOut), a);
539 rcStrict = pfnOutCallback(pDevIns, pvUser, Port, u32Value, (unsigned)cbValue);
540 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfOut), a);
541 }
542 else
543#endif
544 rcStrict = pfnOutCallback(pDevIns, pvUser, Port, u32Value, (unsigned)cbValue);
545 PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo));
546
547#ifdef VBOX_WITH_STATISTICS
548 if (rcStrict == VINF_SUCCESS && pStats)
549 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Out));
550# ifndef IN_RING3
551 else if (rcStrict == VINF_IOM_R3_IOPORT_WRITE && pStats)
552 STAM_COUNTER_INC(&pStats->OutRZToR3);
553# endif
554#endif
555 Log3(("IOMIOPortWrite: Port=%RTiop u32=%08RX32 cb=%d rc=%Rrc\n", Port, u32Value, cbValue, VBOXSTRICTRC_VAL(rcStrict)));
556#ifndef IN_RING3
557 if (rcStrict == VINF_IOM_R3_IOPORT_WRITE)
558 return iomIOPortRing3WritePending(pVCpu, Port, u32Value, cbValue);
559#endif
560 return rcStrict;
561 }
562
563#ifndef IN_RING3
564 /*
565 * Handler in ring-3?
566 */
567 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeR3(pVM, Port);
568 if (pRangeR3)
569 {
570# ifdef VBOX_WITH_STATISTICS
571 if (pStats)
572 STAM_COUNTER_INC(&pStats->OutRZToR3);
573# endif
574 IOM_UNLOCK_SHARED(pVM);
575 return iomIOPortRing3WritePending(pVCpu, Port, u32Value, cbValue);
576 }
577#endif
578
579 /*
580 * Ok, no handler for that port.
581 */
582#ifdef VBOX_WITH_STATISTICS
583 /* statistics. */
584 if (pStats)
585 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Out));
586#endif
587 Log3(("IOMIOPortWrite: Port=%RTiop u32=%08RX32 cb=%d nop\n", Port, u32Value, cbValue));
588 IOM_UNLOCK_SHARED(pVM);
589 return VINF_SUCCESS;
590}
591
592
593/**
594 * Writes the string buffer of an I/O port register.
595 *
596 * @returns Strict VBox status code. Informational status codes other than the one documented
597 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
598 * @retval VINF_SUCCESS Success or no string I/O callback in
599 * this context.
600 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
601 * status code must be passed on to EM.
602 * @retval VINF_IOM_R3_IOPORT_WRITE Defer the write to ring-3. (R0/RC only)
603 *
604 * @param pVM The cross context VM structure.
605 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
606 * @param uPort The port to write to.
607 * @param pvSrc The guest page to read from.
608 * @param pcTransfers Pointer to the number of transfer units to write, on
609 * return remaining transfer units.
610 * @param cb Size of the transfer unit (1, 2 or 4 bytes).
611 */
612VMM_INT_DECL(VBOXSTRICTRC) IOMIOPortWriteString(PVM pVM, PVMCPU pVCpu, RTIOPORT uPort, void const *pvSrc,
613 uint32_t *pcTransfers, unsigned cb)
614{
615 Assert(pVCpu->iom.s.PendingIOPortWrite.cbValue == 0);
616 Assert(cb == 1 || cb == 2 || cb == 4);
617
618 /* Take the IOM lock before performing any device I/O. */
619 int rc2 = IOM_LOCK_SHARED(pVM);
620#ifndef IN_RING3
621 if (rc2 == VERR_SEM_BUSY)
622 return VINF_IOM_R3_IOPORT_WRITE;
623#endif
624 AssertRC(rc2);
625
626 const uint32_t cRequestedTransfers = *pcTransfers;
627 Assert(cRequestedTransfers > 0);
628
629#ifdef VBOX_WITH_STATISTICS
630 /*
631 * Get the statistics record.
632 */
633 PIOMIOPORTSTATS pStats = pVCpu->iom.s.CTX_SUFF(pStatsLastWrite);
634 if (!pStats || pStats->Core.Key != uPort)
635 {
636 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTX_SUFF(pTrees)->IOPortStatTree, uPort);
637 if (pStats)
638 pVCpu->iom.s.CTX_SUFF(pStatsLastWrite) = pStats;
639 }
640#endif
641
642 /*
643 * Get handler for current context.
644 */
645 CTX_SUFF(PIOMIOPORTRANGE) pRange = pVCpu->iom.s.CTX_SUFF(pRangeLastWrite);
646 if ( !pRange
647 || (unsigned)uPort - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
648 {
649 pRange = iomIOPortGetRange(pVM, uPort);
650 if (pRange)
651 pVCpu->iom.s.CTX_SUFF(pRangeLastWrite) = pRange;
652 }
653 MMHYPER_RC_ASSERT_RCPTR(pVM, pRange);
654 if (pRange)
655 {
656 /*
657 * Found a range.
658 */
659 PFNIOMIOPORTOUTSTRING pfnOutStrCallback = pRange->pfnOutStrCallback;
660 PFNIOMIOPORTOUT pfnOutCallback = pRange->pfnOutCallback;
661#ifndef IN_RING3
662 if (pfnOutStrCallback || pfnOutCallback)
663 { /* likely */ }
664 else
665 {
666 STAM_STATS({ if (pStats) STAM_COUNTER_INC(&pStats->OutRZToR3); });
667 IOM_UNLOCK_SHARED(pVM);
668 return VINF_IOM_R3_IOPORT_WRITE;
669 }
670#endif
671 void *pvUser = pRange->pvUser;
672 PPDMDEVINS pDevIns = pRange->pDevIns;
673 IOM_UNLOCK_SHARED(pVM);
674
675 /*
676 * Call the device.
677 */
678 VBOXSTRICTRC rcStrict = PDMCritSectEnter(pDevIns->CTX_SUFF(pCritSectRo), VINF_IOM_R3_IOPORT_WRITE);
679 if (rcStrict == VINF_SUCCESS)
680 { /* likely */ }
681 else
682 {
683 STAM_STATS({ if (pStats) STAM_COUNTER_INC(&pStats->OutRZToR3); });
684 return rcStrict;
685 }
686
687 /*
688 * First using string I/O if possible.
689 */
690 if (pfnOutStrCallback)
691 {
692#ifdef VBOX_WITH_STATISTICS
693 if (pStats)
694 {
695 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfOut), a);
696 rcStrict = pfnOutStrCallback(pDevIns, pvUser, uPort, (uint8_t const *)pvSrc, pcTransfers, cb);
697 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfOut), a);
698 }
699 else
700#endif
701 rcStrict = pfnOutStrCallback(pDevIns, pvUser, uPort, (uint8_t const *)pvSrc, pcTransfers, cb);
702 }
703
704 /*
705 * Then doing the single I/O fallback.
706 */
707 if ( *pcTransfers > 0
708 && rcStrict == VINF_SUCCESS)
709 {
710 pvSrc = (uint8_t *)pvSrc + (cRequestedTransfers - *pcTransfers) * cb;
711 do
712 {
713 uint32_t u32Value;
714 switch (cb)
715 {
716 case 4: u32Value = *(uint32_t *)pvSrc; pvSrc = (uint8_t const *)pvSrc + 4; break;
717 case 2: u32Value = *(uint16_t *)pvSrc; pvSrc = (uint8_t const *)pvSrc + 2; break;
718 case 1: u32Value = *(uint8_t *)pvSrc; pvSrc = (uint8_t const *)pvSrc + 1; break;
719 default: AssertFailed(); u32Value = UINT32_MAX;
720 }
721#ifdef VBOX_WITH_STATISTICS
722 if (pStats)
723 {
724 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfOut), a);
725 rcStrict = pfnOutCallback(pDevIns, pvUser, uPort, u32Value, cb);
726 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfOut), a);
727 }
728 else
729#endif
730 rcStrict = pfnOutCallback(pDevIns, pvUser, uPort, u32Value, cb);
731 if (IOM_SUCCESS(rcStrict))
732 *pcTransfers -= 1;
733 } while ( *pcTransfers > 0
734 && rcStrict == VINF_SUCCESS);
735 }
736
737 PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo));
738
739#ifdef VBOX_WITH_STATISTICS
740 if (rcStrict == VINF_SUCCESS && pStats)
741 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Out));
742# ifndef IN_RING3
743 else if (rcStrict == VINF_IOM_R3_IOPORT_WRITE && pStats)
744 STAM_COUNTER_INC(&pStats->OutRZToR3);
745# endif
746#endif
747 Log3(("IOMIOPortWriteStr: uPort=%RTiop pvSrc=%p pcTransfer=%p:{%#x->%#x} cb=%d rcStrict=%Rrc\n",
748 uPort, pvSrc, pcTransfers, cRequestedTransfers, *pcTransfers, cb, VBOXSTRICTRC_VAL(rcStrict)));
749 return rcStrict;
750 }
751
752#ifndef IN_RING3
753 /*
754 * Handler in ring-3?
755 */
756 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeR3(pVM, uPort);
757 if (pRangeR3)
758 {
759# ifdef VBOX_WITH_STATISTICS
760 if (pStats)
761 STAM_COUNTER_INC(&pStats->OutRZToR3);
762# endif
763 IOM_UNLOCK_SHARED(pVM);
764 return VINF_IOM_R3_IOPORT_WRITE;
765 }
766#endif
767
768 /*
769 * Ok, no handler for this port.
770 */
771 *pcTransfers = 0;
772#ifdef VBOX_WITH_STATISTICS
773 if (pStats)
774 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Out));
775#endif
776 Log3(("IOMIOPortWriteStr: uPort=%RTiop (unused) pvSrc=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=VINF_SUCCESS\n",
777 uPort, pvSrc, pcTransfers, cRequestedTransfers, *pcTransfers, cb));
778 IOM_UNLOCK_SHARED(pVM);
779 return VINF_SUCCESS;
780}
781
782
783/**
784 * Fress an MMIO range after the reference counter has become zero.
785 *
786 * @param pVM The cross context VM structure.
787 * @param pRange The range to free.
788 */
789void iomMmioFreeRange(PVM pVM, PIOMMMIORANGE pRange)
790{
791 MMHyperFree(pVM, pRange);
792}
793
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