VirtualBox

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

Last change on this file since 76553 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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