VirtualBox

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

Last change on this file since 60847 was 60847, checked in by vboxsync, 9 years ago

IOM: New way of defer RC+R0 I/O port writes, prepping for MMIO writes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 31.5 KB
Line 
1/* $Id: IOMAll.cpp 60847 2016-05-05 15:24:46Z vboxsync $ */
2/** @file
3 * IOM - Input / Output Monitor - Any Context.
4 */
5
6/*
7 * Copyright (C) 2006-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. 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 AssertReturn(pVCpu->iom.s.PendingIOPortWrite.cbValue == 0, VERR_IOM_IOPORT_IPE_1);
442 pVCpu->iom.s.PendingIOPortWrite.IOPort = Port;
443 pVCpu->iom.s.PendingIOPortWrite.u32Value = u32Value;
444 pVCpu->iom.s.PendingIOPortWrite.cbValue = (uint32_t)cbValue;
445 VMCPU_FF_SET(pVCpu, VMCPU_FF_IOM);
446 return VINF_IOM_R3_IOPORT_COMMIT_WRITE;
447}
448#endif
449
450
451/**
452 * Writes to an I/O port register.
453 *
454 * @returns Strict VBox status code. Informational status codes other than the one documented
455 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
456 * @retval VINF_SUCCESS Success.
457 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
458 * status code must be passed on to EM.
459 * @retval VINF_IOM_R3_IOPORT_WRITE Defer the write to ring-3. (R0/RC only)
460 *
461 * @param pVM The cross context VM structure.
462 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
463 * @param Port The port to write to.
464 * @param u32Value The value to write.
465 * @param cbValue The size of the register to read in bytes. 1, 2 or 4 bytes.
466 */
467VMMDECL(VBOXSTRICTRC) IOMIOPortWrite(PVM pVM, PVMCPU pVCpu, RTIOPORT Port, uint32_t u32Value, size_t cbValue)
468{
469#ifndef IN_RING3
470 Assert(pVCpu->iom.s.PendingIOPortWrite.cbValue == 0);
471#endif
472
473 /* Take the IOM lock before performing any device I/O. */
474 int rc2 = IOM_LOCK_SHARED(pVM);
475#ifndef IN_RING3
476 if (rc2 == VERR_SEM_BUSY)
477 return iomIOPortRing3WritePending(pVCpu, Port, u32Value, cbValue);
478#endif
479 AssertRC(rc2);
480#if defined(IEM_VERIFICATION_MODE) && defined(IN_RING3)
481 IEMNotifyIOPortWrite(pVM, Port, u32Value, cbValue);
482#endif
483
484/** @todo bird: When I get time, I'll remove the RC/R0 trees and link the RC/R0
485 * entries to the ring-3 node. */
486#ifdef VBOX_WITH_STATISTICS
487 /*
488 * Find the statistics record.
489 */
490 PIOMIOPORTSTATS pStats = pVCpu->iom.s.CTX_SUFF(pStatsLastWrite);
491 if (!pStats || pStats->Core.Key != Port)
492 {
493 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTX_SUFF(pTrees)->IOPortStatTree, Port);
494 if (pStats)
495 pVCpu->iom.s.CTX_SUFF(pStatsLastWrite) = pStats;
496 }
497#endif
498
499 /*
500 * Get handler for current context.
501 */
502 CTX_SUFF(PIOMIOPORTRANGE) pRange = pVCpu->iom.s.CTX_SUFF(pRangeLastWrite);
503 if ( !pRange
504 || (unsigned)Port - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
505 {
506 pRange = iomIOPortGetRange(pVM, Port);
507 if (pRange)
508 pVCpu->iom.s.CTX_SUFF(pRangeLastWrite) = pRange;
509 }
510 MMHYPER_RC_ASSERT_RCPTR(pVM, pRange);
511 if (pRange)
512 {
513 /*
514 * Found a range.
515 */
516 PFNIOMIOPORTOUT pfnOutCallback = pRange->pfnOutCallback;
517#ifndef IN_RING3
518 if (pfnOutCallback)
519 { /* likely */ }
520 else
521 {
522 STAM_STATS({ if (pStats) STAM_COUNTER_INC(&pStats->OutRZToR3); });
523 IOM_UNLOCK_SHARED(pVM);
524 return iomIOPortRing3WritePending(pVCpu, Port, u32Value, cbValue);
525 }
526#endif
527 void *pvUser = pRange->pvUser;
528 PPDMDEVINS pDevIns = pRange->pDevIns;
529 IOM_UNLOCK_SHARED(pVM);
530
531 /*
532 * Call the device.
533 */
534 VBOXSTRICTRC rcStrict = PDMCritSectEnter(pDevIns->CTX_SUFF(pCritSectRo), VINF_IOM_R3_IOPORT_WRITE);
535 if (rcStrict == VINF_SUCCESS)
536 { /* likely */ }
537 else
538 {
539 STAM_STATS({ if (pStats) STAM_COUNTER_INC(&pStats->OutRZToR3); });
540#ifndef IN_RING3
541 if (RT_LIKELY(rcStrict == VINF_IOM_R3_IOPORT_WRITE))
542 return iomIOPortRing3WritePending(pVCpu, Port, u32Value, cbValue);
543#endif
544 return rcStrict;
545 }
546#ifdef VBOX_WITH_STATISTICS
547 if (pStats)
548 {
549 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfOut), a);
550 rcStrict = pfnOutCallback(pDevIns, pvUser, Port, u32Value, (unsigned)cbValue);
551 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfOut), a);
552 }
553 else
554#endif
555 rcStrict = pfnOutCallback(pDevIns, pvUser, Port, u32Value, (unsigned)cbValue);
556 PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo));
557
558#ifdef VBOX_WITH_STATISTICS
559 if (rcStrict == VINF_SUCCESS && pStats)
560 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Out));
561# ifndef IN_RING3
562 else if (rcStrict == VINF_IOM_R3_IOPORT_WRITE && pStats)
563 STAM_COUNTER_INC(&pStats->OutRZToR3);
564# endif
565#endif
566 Log3(("IOMIOPortWrite: Port=%RTiop u32=%08RX32 cb=%d rc=%Rrc\n", Port, u32Value, cbValue, VBOXSTRICTRC_VAL(rcStrict)));
567#ifndef IN_RING3
568 if (rcStrict == VINF_IOM_R3_IOPORT_WRITE)
569 return iomIOPortRing3WritePending(pVCpu, Port, u32Value, cbValue);
570#endif
571 return rcStrict;
572 }
573
574#ifndef IN_RING3
575 /*
576 * Handler in ring-3?
577 */
578 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeR3(pVM, Port);
579 if (pRangeR3)
580 {
581# ifdef VBOX_WITH_STATISTICS
582 if (pStats)
583 STAM_COUNTER_INC(&pStats->OutRZToR3);
584# endif
585 IOM_UNLOCK_SHARED(pVM);
586 return iomIOPortRing3WritePending(pVCpu, Port, u32Value, cbValue);
587 }
588#endif
589
590 /*
591 * Ok, no handler for that port.
592 */
593#ifdef VBOX_WITH_STATISTICS
594 /* statistics. */
595 if (pStats)
596 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Out));
597#endif
598 Log3(("IOMIOPortWrite: Port=%RTiop u32=%08RX32 cb=%d nop\n", Port, u32Value, cbValue));
599 IOM_UNLOCK_SHARED(pVM);
600 return VINF_SUCCESS;
601}
602
603
604/**
605 * Writes the string buffer of an I/O port register.
606 *
607 * @returns Strict VBox status code. Informational status codes other than the one documented
608 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
609 * @retval VINF_SUCCESS Success or no string I/O callback in
610 * this context.
611 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
612 * status code must be passed on to EM.
613 * @retval VINF_IOM_R3_IOPORT_WRITE Defer the write to ring-3. (R0/RC only)
614 *
615 * @param pVM The cross context VM structure.
616 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
617 * @param uPort The port to write to.
618 * @param pvSrc The guest page to read from.
619 * @param pcTransfers Pointer to the number of transfer units to write, on
620 * return remaining transfer units.
621 * @param cb Size of the transfer unit (1, 2 or 4 bytes).
622 */
623VMM_INT_DECL(VBOXSTRICTRC) IOMIOPortWriteString(PVM pVM, PVMCPU pVCpu, RTIOPORT uPort, void const *pvSrc,
624 uint32_t *pcTransfers, unsigned cb)
625{
626 Assert(pVCpu->iom.s.PendingIOPortWrite.cbValue == 0);
627 Assert(cb == 1 || cb == 2 || cb == 4);
628
629 /* Take the IOM lock before performing any device I/O. */
630 int rc2 = IOM_LOCK_SHARED(pVM);
631#ifndef IN_RING3
632 if (rc2 == VERR_SEM_BUSY)
633 return VINF_IOM_R3_IOPORT_WRITE;
634#endif
635 AssertRC(rc2);
636#if defined(IEM_VERIFICATION_MODE) && defined(IN_RING3)
637 IEMNotifyIOPortWriteString(pVM, uPort, pvSrc, *pcTransfers, cb);
638#endif
639
640 const uint32_t cRequestedTransfers = *pcTransfers;
641 Assert(cRequestedTransfers > 0);
642
643#ifdef VBOX_WITH_STATISTICS
644 /*
645 * Get the statistics record.
646 */
647 PIOMIOPORTSTATS pStats = pVCpu->iom.s.CTX_SUFF(pStatsLastWrite);
648 if (!pStats || pStats->Core.Key != uPort)
649 {
650 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTX_SUFF(pTrees)->IOPortStatTree, uPort);
651 if (pStats)
652 pVCpu->iom.s.CTX_SUFF(pStatsLastWrite) = pStats;
653 }
654#endif
655
656 /*
657 * Get handler for current context.
658 */
659 CTX_SUFF(PIOMIOPORTRANGE) pRange = pVCpu->iom.s.CTX_SUFF(pRangeLastWrite);
660 if ( !pRange
661 || (unsigned)uPort - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
662 {
663 pRange = iomIOPortGetRange(pVM, uPort);
664 if (pRange)
665 pVCpu->iom.s.CTX_SUFF(pRangeLastWrite) = pRange;
666 }
667 MMHYPER_RC_ASSERT_RCPTR(pVM, pRange);
668 if (pRange)
669 {
670 /*
671 * Found a range.
672 */
673 PFNIOMIOPORTOUTSTRING pfnOutStrCallback = pRange->pfnOutStrCallback;
674 PFNIOMIOPORTOUT pfnOutCallback = pRange->pfnOutCallback;
675#ifndef IN_RING3
676 if (pfnOutStrCallback || pfnOutCallback)
677 { /* likely */ }
678 else
679 {
680 STAM_STATS({ if (pStats) STAM_COUNTER_INC(&pStats->OutRZToR3); });
681 IOM_UNLOCK_SHARED(pVM);
682 return VINF_IOM_R3_IOPORT_WRITE;
683 }
684#endif
685 void *pvUser = pRange->pvUser;
686 PPDMDEVINS pDevIns = pRange->pDevIns;
687 IOM_UNLOCK_SHARED(pVM);
688
689 /*
690 * Call the device.
691 */
692 VBOXSTRICTRC rcStrict = PDMCritSectEnter(pDevIns->CTX_SUFF(pCritSectRo), VINF_IOM_R3_IOPORT_WRITE);
693 if (rcStrict == VINF_SUCCESS)
694 { /* likely */ }
695 else
696 {
697 STAM_STATS({ if (pStats) STAM_COUNTER_INC(&pStats->OutRZToR3); });
698 return rcStrict;
699 }
700
701 /*
702 * First using string I/O if possible.
703 */
704 if (pfnOutStrCallback)
705 {
706#ifdef VBOX_WITH_STATISTICS
707 if (pStats)
708 {
709 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfOut), a);
710 rcStrict = pfnOutStrCallback(pDevIns, pvUser, uPort, (uint8_t const *)pvSrc, pcTransfers, cb);
711 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfOut), a);
712 }
713 else
714#endif
715 rcStrict = pfnOutStrCallback(pDevIns, pvUser, uPort, (uint8_t const *)pvSrc, pcTransfers, cb);
716 }
717
718 /*
719 * Then doing the single I/O fallback.
720 */
721 if ( *pcTransfers > 0
722 && rcStrict == VINF_SUCCESS)
723 {
724 pvSrc = (uint8_t *)pvSrc + (cRequestedTransfers - *pcTransfers) * cb;
725 do
726 {
727 uint32_t u32Value;
728 switch (cb)
729 {
730 case 4: u32Value = *(uint32_t *)pvSrc; pvSrc = (uint8_t const *)pvSrc + 4; break;
731 case 2: u32Value = *(uint16_t *)pvSrc; pvSrc = (uint8_t const *)pvSrc + 2; break;
732 case 1: u32Value = *(uint8_t *)pvSrc; pvSrc = (uint8_t const *)pvSrc + 1; break;
733 default: AssertFailed(); u32Value = UINT32_MAX;
734 }
735#ifdef VBOX_WITH_STATISTICS
736 if (pStats)
737 {
738 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfOut), a);
739 rcStrict = pfnOutCallback(pDevIns, pvUser, uPort, u32Value, cb);
740 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfOut), a);
741 }
742 else
743#endif
744 rcStrict = pfnOutCallback(pDevIns, pvUser, uPort, u32Value, cb);
745 if (IOM_SUCCESS(rcStrict))
746 *pcTransfers -= 1;
747 } while ( *pcTransfers > 0
748 && rcStrict == VINF_SUCCESS);
749 }
750
751 PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo));
752
753#ifdef VBOX_WITH_STATISTICS
754 if (rcStrict == VINF_SUCCESS && pStats)
755 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Out));
756# ifndef IN_RING3
757 else if (rcStrict == VINF_IOM_R3_IOPORT_WRITE && pStats)
758 STAM_COUNTER_INC(&pStats->OutRZToR3);
759# endif
760#endif
761 Log3(("IOMIOPortWriteStr: uPort=%RTiop pvSrc=%p pcTransfer=%p:{%#x->%#x} cb=%d rcStrict=%Rrc\n",
762 uPort, pvSrc, pcTransfers, cRequestedTransfers, *pcTransfers, cb, VBOXSTRICTRC_VAL(rcStrict)));
763 return rcStrict;
764 }
765
766#ifndef IN_RING3
767 /*
768 * Handler in ring-3?
769 */
770 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeR3(pVM, uPort);
771 if (pRangeR3)
772 {
773# ifdef VBOX_WITH_STATISTICS
774 if (pStats)
775 STAM_COUNTER_INC(&pStats->OutRZToR3);
776# endif
777 IOM_UNLOCK_SHARED(pVM);
778 return VINF_IOM_R3_IOPORT_WRITE;
779 }
780#endif
781
782 /*
783 * Ok, no handler for this port.
784 */
785 *pcTransfers = 0;
786#ifdef VBOX_WITH_STATISTICS
787 if (pStats)
788 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Out));
789#endif
790 Log3(("IOMIOPortWriteStr: uPort=%RTiop (unused) pvSrc=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=VINF_SUCCESS\n",
791 uPort, pvSrc, pcTransfers, cRequestedTransfers, *pcTransfers, cb));
792 IOM_UNLOCK_SHARED(pVM);
793 return VINF_SUCCESS;
794}
795
796
797/**
798 * Checks that the operation is allowed according to the IOPL
799 * level and I/O bitmap.
800 *
801 * @returns Strict VBox status code. Informational status codes other than the one documented
802 * here are to be treated as internal failure.
803 * @retval VINF_SUCCESS Success.
804 * @retval VINF_EM_RAW_GUEST_TRAP The exception was left pending. (TRPMRaiseXcptErr)
805 * @retval VINF_TRPM_XCPT_DISPATCHED The exception was raised and dispatched for raw-mode execution. (TRPMRaiseXcptErr)
806 * @retval VINF_EM_RESCHEDULE_REM The exception was dispatched and cannot be executed in raw-mode. (TRPMRaiseXcptErr)
807 *
808 * @param pVM The cross context VM structure.
809 * @param pCtxCore Pointer to register frame.
810 * @param Port The I/O port number.
811 * @param cb The access size.
812 */
813VMMDECL(VBOXSTRICTRC) IOMInterpretCheckPortIOAccess(PVM pVM, PCPUMCTXCORE pCtxCore, RTIOPORT Port, unsigned cb)
814{
815 PVMCPU pVCpu = VMMGetCpu(pVM);
816
817 /*
818 * If this isn't ring-0, we have to check for I/O privileges.
819 */
820 uint32_t efl = CPUMRawGetEFlags(pVCpu);
821 uint32_t cpl = CPUMGetGuestCPL(pVCpu);
822
823 if ( ( cpl > 0
824 && X86_EFL_GET_IOPL(efl) < cpl)
825 || pCtxCore->eflags.Bits.u1VM /* IOPL is ignored in V86 mode; always check TSS bitmap */
826 )
827 {
828 /*
829 * Get TSS location and check if there can be a I/O bitmap.
830 */
831 RTGCUINTPTR GCPtrTss;
832 RTGCUINTPTR cbTss;
833 bool fCanHaveIOBitmap;
834 int rc2 = SELMGetTSSInfo(pVM, pVCpu, &GCPtrTss, &cbTss, &fCanHaveIOBitmap);
835 if (RT_FAILURE(rc2))
836 {
837 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d %Rrc -> #GP(0)\n", Port, cb, rc2));
838 return TRPMRaiseXcptErr(pVCpu, pCtxCore, X86_XCPT_GP, 0);
839 }
840
841 if ( !fCanHaveIOBitmap
842 || cbTss <= sizeof(VBOXTSS)) /** @todo r=bird: Should this really include the interrupt redirection bitmap? */
843 {
844 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d cbTss=%#x fCanHaveIOBitmap=%RTbool -> #GP(0)\n",
845 Port, cb, cbTss, fCanHaveIOBitmap));
846 return TRPMRaiseXcptErr(pVCpu, pCtxCore, X86_XCPT_GP, 0);
847 }
848
849 /*
850 * Fetch the I/O bitmap offset.
851 */
852 uint16_t offIOPB;
853 VBOXSTRICTRC rcStrict = PGMPhysInterpretedRead(pVCpu, pCtxCore, &offIOPB, GCPtrTss + RT_OFFSETOF(VBOXTSS, offIoBitmap), sizeof(offIOPB));
854 if (rcStrict != VINF_SUCCESS)
855 {
856 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d GCPtrTss=%RGv %Rrc\n",
857 Port, cb, GCPtrTss, VBOXSTRICTRC_VAL(rcStrict)));
858 return rcStrict;
859 }
860
861 /*
862 * Check the limit and read the two bitmap bytes.
863 */
864 uint32_t offTss = offIOPB + (Port >> 3);
865 if (offTss + 1 >= cbTss)
866 {
867 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d offTss=%#x cbTss=%#x -> #GP(0)\n",
868 Port, cb, offTss, cbTss));
869 return TRPMRaiseXcptErr(pVCpu, pCtxCore, X86_XCPT_GP, 0);
870 }
871 uint16_t u16;
872 rcStrict = PGMPhysInterpretedRead(pVCpu, pCtxCore, &u16, GCPtrTss + offTss, sizeof(u16));
873 if (rcStrict != VINF_SUCCESS)
874 {
875 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d GCPtrTss=%RGv offTss=%#x -> %Rrc\n",
876 Port, cb, GCPtrTss, offTss, VBOXSTRICTRC_VAL(rcStrict)));
877 return rcStrict;
878 }
879
880 /*
881 * All the bits must be clear.
882 */
883 if ((u16 >> (Port & 7)) & ((1 << cb) - 1))
884 {
885 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d u16=%#x (offTss=%#x) -> #GP(0)\n",
886 Port, cb, u16, offTss));
887 return TRPMRaiseXcptErr(pVCpu, pCtxCore, X86_XCPT_GP, 0);
888 }
889 LogFlow(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d offTss=%#x cbTss=%#x u16=%#x -> OK\n",
890 Port, cb, u16, offTss, cbTss));
891 }
892 return VINF_SUCCESS;
893}
894
895
896/**
897 * Fress an MMIO range after the reference counter has become zero.
898 *
899 * @param pVM The cross context VM structure.
900 * @param pRange The range to free.
901 */
902void iomMmioFreeRange(PVM pVM, PIOMMMIORANGE pRange)
903{
904 MMHyperFree(pVM, pRange);
905}
906
Note: See TracBrowser for help on using the repository browser.

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