VirtualBox

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

最後變更 在這個檔案從80317是 80281,由 vboxsync 提交於 6 年 前

VMM,++: Refactoring code to use VMMC & VMMCPUCC. bugref:9217

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette