VirtualBox

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

Last change on this file since 70898 was 69111, checked in by vboxsync, 7 years ago

(C) year

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