VirtualBox

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

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

IOMIOPortReadString,IOMIOPortWriteString: Must return to ring-3 when there aren't ring-0/raw-mode handlers for a range, and set *pcTransfers to zero for unused ports (read also store 0xff in pvDst).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 30.0 KB
Line 
1/* $Id: IOMAll.cpp 58354 2015-10-21 10:13:47Z 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.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/** @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#if defined(IEM_VERIFICATION_MODE) && defined(IN_RING3)
91 IEMNotifyIOPortRead(pVM, Port, cbValue);
92#endif
93
94#ifdef VBOX_WITH_STATISTICS
95 /*
96 * Get the statistics record.
97 */
98 PIOMIOPORTSTATS pStats = pVCpu->iom.s.CTX_SUFF(pStatsLastRead);
99 if (!pStats || pStats->Core.Key != Port)
100 {
101 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTX_SUFF(pTrees)->IOPortStatTree, Port);
102 if (pStats)
103 pVCpu->iom.s.CTX_SUFF(pStatsLastRead) = pStats;
104 }
105#endif
106
107 /*
108 * Get handler for current context.
109 */
110 CTX_SUFF(PIOMIOPORTRANGE) pRange = pVCpu->iom.s.CTX_SUFF(pRangeLastRead);
111 if ( !pRange
112 || (unsigned)Port - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
113 {
114 pRange = iomIOPortGetRange(pVM, Port);
115 if (pRange)
116 pVCpu->iom.s.CTX_SUFF(pRangeLastRead) = pRange;
117 }
118 MMHYPER_RC_ASSERT_RCPTR(pVM, pRange);
119 if (pRange)
120 {
121 /*
122 * Found a range, get the data in case we leave the IOM lock.
123 */
124 PFNIOMIOPORTIN pfnInCallback = pRange->pfnInCallback;
125#ifndef IN_RING3
126 if (pfnInCallback)
127 { /* likely */ }
128 else
129 {
130 STAM_STATS({ if (pStats) STAM_COUNTER_INC(&pStats->InRZToR3); });
131 IOM_UNLOCK_SHARED(pVM);
132 return VINF_IOM_R3_IOPORT_READ;
133 }
134#endif
135 void *pvUser = pRange->pvUser;
136 PPDMDEVINS pDevIns = pRange->pDevIns;
137 IOM_UNLOCK_SHARED(pVM);
138
139 /*
140 * Call the device.
141 */
142 VBOXSTRICTRC rcStrict = PDMCritSectEnter(pDevIns->CTX_SUFF(pCritSectRo), VINF_IOM_R3_IOPORT_READ);
143 if (rcStrict == VINF_SUCCESS)
144 { /* likely */ }
145 else
146 {
147 STAM_STATS({ if (pStats) STAM_COUNTER_INC(&pStats->InRZToR3); });
148 return rcStrict;
149 }
150#ifdef VBOX_WITH_STATISTICS
151 if (pStats)
152 {
153 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfIn), a);
154 rcStrict = pfnInCallback(pDevIns, pvUser, Port, pu32Value, (unsigned)cbValue);
155 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfIn), a);
156 }
157 else
158#endif
159 rcStrict = pfnInCallback(pDevIns, pvUser, Port, pu32Value, (unsigned)cbValue);
160 PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo));
161
162#ifdef VBOX_WITH_STATISTICS
163 if (rcStrict == VINF_SUCCESS && pStats)
164 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(In));
165# ifndef IN_RING3
166 else if (rcStrict == VINF_IOM_R3_IOPORT_READ && pStats)
167 STAM_COUNTER_INC(&pStats->InRZToR3);
168# endif
169#endif
170 if (rcStrict == VERR_IOM_IOPORT_UNUSED)
171 {
172 /* make return value */
173 rcStrict = VINF_SUCCESS;
174 switch (cbValue)
175 {
176 case 1: *(uint8_t *)pu32Value = 0xff; break;
177 case 2: *(uint16_t *)pu32Value = 0xffff; break;
178 case 4: *(uint32_t *)pu32Value = UINT32_C(0xffffffff); break;
179 default:
180 AssertMsgFailed(("Invalid I/O port size %d. Port=%d\n", cbValue, Port));
181 return VERR_IOM_INVALID_IOPORT_SIZE;
182 }
183 }
184 Log3(("IOMIOPortRead: Port=%RTiop *pu32=%08RX32 cb=%d rc=%Rrc\n", Port, *pu32Value, cbValue, VBOXSTRICTRC_VAL(rcStrict)));
185 return rcStrict;
186 }
187
188#ifndef IN_RING3
189 /*
190 * Handler in ring-3?
191 */
192 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeR3(pVM, Port);
193 if (pRangeR3)
194 {
195# ifdef VBOX_WITH_STATISTICS
196 if (pStats)
197 STAM_COUNTER_INC(&pStats->InRZToR3);
198# endif
199 IOM_UNLOCK_SHARED(pVM);
200 return VINF_IOM_R3_IOPORT_READ;
201 }
202#endif
203
204 /*
205 * Ok, no handler for this port.
206 */
207#ifdef VBOX_WITH_STATISTICS
208 if (pStats)
209 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(In));
210#endif
211
212 /* make return value */
213 switch (cbValue)
214 {
215 case 1: *(uint8_t *)pu32Value = 0xff; break;
216 case 2: *(uint16_t *)pu32Value = 0xffff; break;
217 case 4: *(uint32_t *)pu32Value = UINT32_C(0xffffffff); break;
218 default:
219 AssertMsgFailed(("Invalid I/O port size %d. Port=%d\n", cbValue, Port));
220 IOM_UNLOCK_SHARED(pVM);
221 return VERR_IOM_INVALID_IOPORT_SIZE;
222 }
223 Log3(("IOMIOPortRead: Port=%RTiop *pu32=%08RX32 cb=%d rc=VINF_SUCCESS\n", Port, *pu32Value, cbValue));
224 IOM_UNLOCK_SHARED(pVM);
225 return VINF_SUCCESS;
226}
227
228
229/**
230 * Reads the string buffer of an I/O port register.
231 *
232 * @returns Strict VBox status code. Informational status codes other than the one documented
233 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
234 * @retval VINF_SUCCESS Success or no string I/O callback in
235 * this context.
236 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
237 * status code must be passed on to EM.
238 * @retval VINF_IOM_R3_IOPORT_READ Defer the read to ring-3. (R0/RC only)
239 *
240 * @param pVM The cross context VM structure.
241 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
242 * @param uPort The port to read.
243 * @param pvDst Pointer to the destination buffer.
244 * @param pcTransfers Pointer to the number of transfer units to read, on return remaining transfer units.
245 * @param cb Size of the transfer unit (1, 2 or 4 bytes).
246 */
247VMM_INT_DECL(VBOXSTRICTRC) IOMIOPortReadString(PVM pVM, PVMCPU pVCpu, RTIOPORT uPort,
248 void *pvDst, uint32_t *pcTransfers, unsigned cb)
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#if defined(IEM_VERIFICATION_MODE) && defined(IN_RING3)
258 IEMNotifyIOPortReadString(pVM, uPort, pvDst, *pcTransfers, cb);
259#endif
260
261 const uint32_t cRequestedTransfers = *pcTransfers;
262 Assert(cRequestedTransfers > 0);
263
264#ifdef VBOX_WITH_STATISTICS
265 /*
266 * Get the statistics record.
267 */
268 PIOMIOPORTSTATS pStats = pVCpu->iom.s.CTX_SUFF(pStatsLastRead);
269 if (!pStats || pStats->Core.Key != uPort)
270 {
271 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTX_SUFF(pTrees)->IOPortStatTree, uPort);
272 if (pStats)
273 pVCpu->iom.s.CTX_SUFF(pStatsLastRead) = pStats;
274 }
275#endif
276
277 /*
278 * Get handler for current context.
279 */
280 CTX_SUFF(PIOMIOPORTRANGE) pRange = pVCpu->iom.s.CTX_SUFF(pRangeLastRead);
281 if ( !pRange
282 || (unsigned)uPort - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
283 {
284 pRange = iomIOPortGetRange(pVM, uPort);
285 if (pRange)
286 pVCpu->iom.s.CTX_SUFF(pRangeLastRead) = pRange;
287 }
288 MMHYPER_RC_ASSERT_RCPTR(pVM, pRange);
289 if (pRange)
290 {
291 /*
292 * Found a range.
293 */
294 PFNIOMIOPORTINSTRING pfnInStrCallback = pRange->pfnInStrCallback;
295 PFNIOMIOPORTIN pfnInCallback = pRange->pfnInCallback;
296#ifndef IN_RING3
297 if (pfnInStrCallback || pfnInCallback)
298 { /* likely */ }
299 else
300 {
301 STAM_STATS({ if (pStats) STAM_COUNTER_INC(&pStats->InRZToR3); });
302 IOM_UNLOCK_SHARED(pVM);
303 return VINF_IOM_R3_IOPORT_READ;
304 }
305#endif
306 void *pvUser = pRange->pvUser;
307 PPDMDEVINS pDevIns = pRange->pDevIns;
308 IOM_UNLOCK_SHARED(pVM);
309
310 /*
311 * Call the device.
312 */
313 VBOXSTRICTRC rcStrict = PDMCritSectEnter(pDevIns->CTX_SUFF(pCritSectRo), VINF_IOM_R3_IOPORT_READ);
314 if (rcStrict == VINF_SUCCESS)
315 { /* likely */ }
316 else
317 {
318 STAM_STATS({ if (pStats) STAM_COUNTER_INC(&pStats->InRZToR3); });
319 return rcStrict;
320 }
321
322 /*
323 * First using the string I/O callback.
324 */
325 if (pfnInStrCallback)
326 {
327#ifdef VBOX_WITH_STATISTICS
328 if (pStats)
329 {
330 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfIn), a);
331 rcStrict = pfnInStrCallback(pDevIns, pvUser, uPort, (uint8_t *)pvDst, pcTransfers, cb);
332 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfIn), a);
333 }
334 else
335#endif
336 rcStrict = pfnInStrCallback(pDevIns, pvUser, uPort, (uint8_t *)pvDst, pcTransfers, cb);
337 }
338
339 /*
340 * Then doing the single I/O fallback.
341 */
342 if ( *pcTransfers > 0
343 && rcStrict == VINF_SUCCESS)
344 {
345 pvDst = (uint8_t *)pvDst + (cRequestedTransfers - *pcTransfers) * cb;
346 do
347 {
348 uint32_t u32Value = 0;
349#ifdef VBOX_WITH_STATISTICS
350 if (pStats)
351 {
352 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfIn), a);
353 rcStrict = pfnInCallback(pDevIns, pvUser, uPort, &u32Value, cb);
354 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfIn), a);
355 }
356 else
357#endif
358 rcStrict = pfnInCallback(pDevIns, pvUser, uPort, &u32Value, cb);
359 if (rcStrict == VERR_IOM_IOPORT_UNUSED)
360 {
361 u32Value = UINT32_MAX;
362 rcStrict = VINF_SUCCESS;
363 }
364 if (IOM_SUCCESS(rcStrict))
365 {
366 switch (cb)
367 {
368 case 4: *(uint32_t *)pvDst = u32Value; pvDst = (uint8_t *)pvDst + 4; break;
369 case 2: *(uint16_t *)pvDst = (uint16_t)u32Value; pvDst = (uint8_t *)pvDst + 2; break;
370 case 1: *(uint8_t *)pvDst = (uint8_t )u32Value; pvDst = (uint8_t *)pvDst + 1; break;
371 default: AssertFailed();
372 }
373 *pcTransfers -= 1;
374 }
375 } while ( *pcTransfers > 0
376 && rcStrict == VINF_SUCCESS);
377 }
378 PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo));
379
380#ifdef VBOX_WITH_STATISTICS
381 if (rcStrict == VINF_SUCCESS && pStats)
382 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(In));
383# ifndef IN_RING3
384 else if (rcStrict == VINF_IOM_R3_IOPORT_READ && pStats)
385 STAM_COUNTER_INC(&pStats->InRZToR3);
386# endif
387#endif
388 Log3(("IOMIOPortReadStr: uPort=%RTiop pvDst=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=%Rrc\n",
389 uPort, pvDst, pcTransfers, cRequestedTransfers, *pcTransfers, cb, VBOXSTRICTRC_VAL(rcStrict)));
390 return rcStrict;
391 }
392
393#ifndef IN_RING3
394 /*
395 * Handler in ring-3?
396 */
397 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeR3(pVM, uPort);
398 if (pRangeR3)
399 {
400# ifdef VBOX_WITH_STATISTICS
401 if (pStats)
402 STAM_COUNTER_INC(&pStats->InRZToR3);
403# endif
404 IOM_UNLOCK_SHARED(pVM);
405 return VINF_IOM_R3_IOPORT_READ;
406 }
407#endif
408
409 /*
410 * Ok, no handler for this port.
411 */
412 *pcTransfers = 0;
413 memset(pvDst, 0xff, cRequestedTransfers * cb);
414#ifdef VBOX_WITH_STATISTICS
415 if (pStats)
416 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(In));
417#endif
418 Log3(("IOMIOPortReadStr: uPort=%RTiop (unused) pvDst=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=VINF_SUCCESS\n",
419 uPort, pvDst, pcTransfers, cRequestedTransfers, *pcTransfers, cb));
420 IOM_UNLOCK_SHARED(pVM);
421 return VINF_SUCCESS;
422}
423
424
425/**
426 * Writes to an I/O port register.
427 *
428 * @returns Strict VBox status code. Informational status codes other than the one documented
429 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
430 * @retval VINF_SUCCESS Success.
431 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
432 * status code must be passed on to EM.
433 * @retval VINF_IOM_R3_IOPORT_WRITE Defer the write to ring-3. (R0/RC only)
434 *
435 * @param pVM The cross context VM structure.
436 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
437 * @param Port The port to write to.
438 * @param u32Value The value to write.
439 * @param cbValue The size of the register to read in bytes. 1, 2 or 4 bytes.
440 */
441VMMDECL(VBOXSTRICTRC) IOMIOPortWrite(PVM pVM, PVMCPU pVCpu, RTIOPORT Port, uint32_t u32Value, size_t cbValue)
442{
443 /* Take the IOM lock before performing any device I/O. */
444 int rc2 = IOM_LOCK_SHARED(pVM);
445#ifndef IN_RING3
446 if (rc2 == VERR_SEM_BUSY)
447 return VINF_IOM_R3_IOPORT_WRITE;
448#endif
449 AssertRC(rc2);
450#if defined(IEM_VERIFICATION_MODE) && defined(IN_RING3)
451 IEMNotifyIOPortWrite(pVM, Port, u32Value, cbValue);
452#endif
453
454/** @todo bird: When I get time, I'll remove the RC/R0 trees and link the RC/R0
455 * entries to the ring-3 node. */
456#ifdef VBOX_WITH_STATISTICS
457 /*
458 * Find the statistics record.
459 */
460 PIOMIOPORTSTATS pStats = pVCpu->iom.s.CTX_SUFF(pStatsLastWrite);
461 if (!pStats || pStats->Core.Key != Port)
462 {
463 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTX_SUFF(pTrees)->IOPortStatTree, Port);
464 if (pStats)
465 pVCpu->iom.s.CTX_SUFF(pStatsLastWrite) = pStats;
466 }
467#endif
468
469 /*
470 * Get handler for current context.
471 */
472 CTX_SUFF(PIOMIOPORTRANGE) pRange = pVCpu->iom.s.CTX_SUFF(pRangeLastWrite);
473 if ( !pRange
474 || (unsigned)Port - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
475 {
476 pRange = iomIOPortGetRange(pVM, Port);
477 if (pRange)
478 pVCpu->iom.s.CTX_SUFF(pRangeLastWrite) = pRange;
479 }
480 MMHYPER_RC_ASSERT_RCPTR(pVM, pRange);
481 if (pRange)
482 {
483 /*
484 * Found a range.
485 */
486 PFNIOMIOPORTOUT pfnOutCallback = pRange->pfnOutCallback;
487#ifndef IN_RING3
488 if (pfnOutCallback)
489 { /* likely */ }
490 else
491 {
492 STAM_STATS({ if (pStats) STAM_COUNTER_INC(&pStats->OutRZToR3); });
493 IOM_UNLOCK_SHARED(pVM);
494 return VINF_IOM_R3_IOPORT_WRITE;
495 }
496#endif
497 void *pvUser = pRange->pvUser;
498 PPDMDEVINS pDevIns = pRange->pDevIns;
499 IOM_UNLOCK_SHARED(pVM);
500
501 /*
502 * Call the device.
503 */
504 VBOXSTRICTRC rcStrict = PDMCritSectEnter(pDevIns->CTX_SUFF(pCritSectRo), VINF_IOM_R3_IOPORT_WRITE);
505 if (rcStrict == VINF_SUCCESS)
506 { /* likely */ }
507 else
508 {
509 STAM_STATS({ if (pStats) STAM_COUNTER_INC(&pStats->OutRZToR3); });
510 return rcStrict;
511 }
512#ifdef VBOX_WITH_STATISTICS
513 if (pStats)
514 {
515 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfOut), a);
516 rcStrict = pfnOutCallback(pDevIns, pvUser, Port, u32Value, (unsigned)cbValue);
517 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfOut), a);
518 }
519 else
520#endif
521 rcStrict = pfnOutCallback(pDevIns, pvUser, Port, u32Value, (unsigned)cbValue);
522 PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo));
523
524#ifdef VBOX_WITH_STATISTICS
525 if (rcStrict == VINF_SUCCESS && pStats)
526 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Out));
527# ifndef IN_RING3
528 else if (rcStrict == VINF_IOM_R3_IOPORT_WRITE && pStats)
529 STAM_COUNTER_INC(&pStats->OutRZToR3);
530# endif
531#endif
532 Log3(("IOMIOPortWrite: Port=%RTiop u32=%08RX32 cb=%d rc=%Rrc\n", Port, u32Value, cbValue, VBOXSTRICTRC_VAL(rcStrict)));
533 return rcStrict;
534 }
535
536#ifndef IN_RING3
537 /*
538 * Handler in ring-3?
539 */
540 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeR3(pVM, Port);
541 if (pRangeR3)
542 {
543# ifdef VBOX_WITH_STATISTICS
544 if (pStats)
545 STAM_COUNTER_INC(&pStats->OutRZToR3);
546# endif
547 IOM_UNLOCK_SHARED(pVM);
548 return VINF_IOM_R3_IOPORT_WRITE;
549 }
550#endif
551
552 /*
553 * Ok, no handler for that port.
554 */
555#ifdef VBOX_WITH_STATISTICS
556 /* statistics. */
557 if (pStats)
558 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Out));
559#endif
560 Log3(("IOMIOPortWrite: Port=%RTiop u32=%08RX32 cb=%d nop\n", Port, u32Value, cbValue));
561 IOM_UNLOCK_SHARED(pVM);
562 return VINF_SUCCESS;
563}
564
565
566/**
567 * Writes the string buffer of an I/O port register.
568 *
569 * @returns Strict VBox status code. Informational status codes other than the one documented
570 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
571 * @retval VINF_SUCCESS Success or no string I/O callback in
572 * this context.
573 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
574 * status code must be passed on to EM.
575 * @retval VINF_IOM_R3_IOPORT_WRITE Defer the write to ring-3. (R0/RC only)
576 *
577 * @param pVM The cross context VM structure.
578 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
579 * @param uPort The port to write to.
580 * @param pvSrc The guest page to read from.
581 * @param pcTransfers Pointer to the number of transfer units to write, on
582 * return remaining transfer units.
583 * @param cb Size of the transfer unit (1, 2 or 4 bytes).
584 */
585VMM_INT_DECL(VBOXSTRICTRC) IOMIOPortWriteString(PVM pVM, PVMCPU pVCpu, RTIOPORT uPort, void const *pvSrc,
586 uint32_t *pcTransfers, unsigned cb)
587{
588 Assert(cb == 1 || cb == 2 || cb == 4);
589
590 /* Take the IOM lock before performing any device I/O. */
591 int rc2 = IOM_LOCK_SHARED(pVM);
592#ifndef IN_RING3
593 if (rc2 == VERR_SEM_BUSY)
594 return VINF_IOM_R3_IOPORT_WRITE;
595#endif
596 AssertRC(rc2);
597#if defined(IEM_VERIFICATION_MODE) && defined(IN_RING3)
598 IEMNotifyIOPortWriteString(pVM, uPort, pvSrc, *pcTransfers, cb);
599#endif
600
601 const uint32_t cRequestedTransfers = *pcTransfers;
602 Assert(cRequestedTransfers > 0);
603
604#ifdef VBOX_WITH_STATISTICS
605 /*
606 * Get the statistics record.
607 */
608 PIOMIOPORTSTATS pStats = pVCpu->iom.s.CTX_SUFF(pStatsLastWrite);
609 if (!pStats || pStats->Core.Key != uPort)
610 {
611 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTX_SUFF(pTrees)->IOPortStatTree, uPort);
612 if (pStats)
613 pVCpu->iom.s.CTX_SUFF(pStatsLastWrite) = pStats;
614 }
615#endif
616
617 /*
618 * Get handler for current context.
619 */
620 CTX_SUFF(PIOMIOPORTRANGE) pRange = pVCpu->iom.s.CTX_SUFF(pRangeLastWrite);
621 if ( !pRange
622 || (unsigned)uPort - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
623 {
624 pRange = iomIOPortGetRange(pVM, uPort);
625 if (pRange)
626 pVCpu->iom.s.CTX_SUFF(pRangeLastWrite) = pRange;
627 }
628 MMHYPER_RC_ASSERT_RCPTR(pVM, pRange);
629 if (pRange)
630 {
631 /*
632 * Found a range.
633 */
634 PFNIOMIOPORTOUTSTRING pfnOutStrCallback = pRange->pfnOutStrCallback;
635 PFNIOMIOPORTOUT pfnOutCallback = pRange->pfnOutCallback;
636#ifndef IN_RING3
637 if (pfnOutStrCallback || pfnOutCallback)
638 { /* likely */ }
639 else
640 {
641 STAM_STATS({ if (pStats) STAM_COUNTER_INC(&pStats->OutRZToR3); });
642 IOM_UNLOCK_SHARED(pVM);
643 return VINF_IOM_R3_IOPORT_WRITE;
644 }
645#endif
646 void *pvUser = pRange->pvUser;
647 PPDMDEVINS pDevIns = pRange->pDevIns;
648 IOM_UNLOCK_SHARED(pVM);
649
650 /*
651 * Call the device.
652 */
653 VBOXSTRICTRC rcStrict = PDMCritSectEnter(pDevIns->CTX_SUFF(pCritSectRo), VINF_IOM_R3_IOPORT_WRITE);
654 if (rcStrict == VINF_SUCCESS)
655 { /* likely */ }
656 else
657 {
658 STAM_STATS({ if (pStats) STAM_COUNTER_INC(&pStats->OutRZToR3); });
659 return rcStrict;
660 }
661
662 /*
663 * First using string I/O if possible.
664 */
665 if (pfnOutStrCallback)
666 {
667#ifdef VBOX_WITH_STATISTICS
668 if (pStats)
669 {
670 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfOut), a);
671 rcStrict = pfnOutStrCallback(pDevIns, pvUser, uPort, (uint8_t const *)pvSrc, pcTransfers, cb);
672 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfOut), a);
673 }
674 else
675#endif
676 rcStrict = pfnOutStrCallback(pDevIns, pvUser, uPort, (uint8_t const *)pvSrc, pcTransfers, cb);
677 }
678
679 /*
680 * Then doing the single I/O fallback.
681 */
682 if ( *pcTransfers > 0
683 && rcStrict == VINF_SUCCESS)
684 {
685 pvSrc = (uint8_t *)pvSrc + (cRequestedTransfers - *pcTransfers) * cb;
686 do
687 {
688 uint32_t u32Value;
689 switch (cb)
690 {
691 case 4: u32Value = *(uint32_t *)pvSrc; pvSrc = (uint8_t const *)pvSrc + 4; break;
692 case 2: u32Value = *(uint16_t *)pvSrc; pvSrc = (uint8_t const *)pvSrc + 2; break;
693 case 1: u32Value = *(uint8_t *)pvSrc; pvSrc = (uint8_t const *)pvSrc + 1; break;
694 default: AssertFailed(); u32Value = UINT32_MAX;
695 }
696#ifdef VBOX_WITH_STATISTICS
697 if (pStats)
698 {
699 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfOut), a);
700 rcStrict = pfnOutCallback(pDevIns, pvUser, uPort, u32Value, cb);
701 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfOut), a);
702 }
703 else
704#endif
705 rcStrict = pfnOutCallback(pDevIns, pvUser, uPort, u32Value, cb);
706 if (IOM_SUCCESS(rcStrict))
707 *pcTransfers -= 1;
708 } while ( *pcTransfers > 0
709 && rcStrict == VINF_SUCCESS);
710 }
711
712 PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo));
713
714#ifdef VBOX_WITH_STATISTICS
715 if (rcStrict == VINF_SUCCESS && pStats)
716 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Out));
717# ifndef IN_RING3
718 else if (rcStrict == VINF_IOM_R3_IOPORT_WRITE && pStats)
719 STAM_COUNTER_INC(&pStats->OutRZToR3);
720# endif
721#endif
722 Log3(("IOMIOPortWriteStr: uPort=%RTiop pvSrc=%p pcTransfer=%p:{%#x->%#x} cb=%d rcStrict=%Rrc\n",
723 uPort, pvSrc, pcTransfers, cRequestedTransfers, *pcTransfers, cb, VBOXSTRICTRC_VAL(rcStrict)));
724 return rcStrict;
725 }
726
727#ifndef IN_RING3
728 /*
729 * Handler in ring-3?
730 */
731 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeR3(pVM, uPort);
732 if (pRangeR3)
733 {
734# ifdef VBOX_WITH_STATISTICS
735 if (pStats)
736 STAM_COUNTER_INC(&pStats->OutRZToR3);
737# endif
738 IOM_UNLOCK_SHARED(pVM);
739 return VINF_IOM_R3_IOPORT_WRITE;
740 }
741#endif
742
743 /*
744 * Ok, no handler for this port.
745 */
746 *pcTransfers = 0;
747#ifdef VBOX_WITH_STATISTICS
748 if (pStats)
749 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Out));
750#endif
751 Log3(("IOMIOPortWriteStr: uPort=%RTiop (unused) pvSrc=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=VINF_SUCCESS\n",
752 uPort, pvSrc, pcTransfers, cRequestedTransfers, *pcTransfers, cb));
753 IOM_UNLOCK_SHARED(pVM);
754 return VINF_SUCCESS;
755}
756
757
758/**
759 * Checks that the operation is allowed according to the IOPL
760 * level and I/O bitmap.
761 *
762 * @returns Strict VBox status code. Informational status codes other than the one documented
763 * here are to be treated as internal failure.
764 * @retval VINF_SUCCESS Success.
765 * @retval VINF_EM_RAW_GUEST_TRAP The exception was left pending. (TRPMRaiseXcptErr)
766 * @retval VINF_TRPM_XCPT_DISPATCHED The exception was raised and dispatched for raw-mode execution. (TRPMRaiseXcptErr)
767 * @retval VINF_EM_RESCHEDULE_REM The exception was dispatched and cannot be executed in raw-mode. (TRPMRaiseXcptErr)
768 *
769 * @param pVM The cross context VM structure.
770 * @param pCtxCore Pointer to register frame.
771 * @param Port The I/O port number.
772 * @param cb The access size.
773 */
774VMMDECL(VBOXSTRICTRC) IOMInterpretCheckPortIOAccess(PVM pVM, PCPUMCTXCORE pCtxCore, RTIOPORT Port, unsigned cb)
775{
776 PVMCPU pVCpu = VMMGetCpu(pVM);
777
778 /*
779 * If this isn't ring-0, we have to check for I/O privileges.
780 */
781 uint32_t efl = CPUMRawGetEFlags(pVCpu);
782 uint32_t cpl = CPUMGetGuestCPL(pVCpu);
783
784 if ( ( cpl > 0
785 && X86_EFL_GET_IOPL(efl) < cpl)
786 || pCtxCore->eflags.Bits.u1VM /* IOPL is ignored in V86 mode; always check TSS bitmap */
787 )
788 {
789 /*
790 * Get TSS location and check if there can be a I/O bitmap.
791 */
792 RTGCUINTPTR GCPtrTss;
793 RTGCUINTPTR cbTss;
794 bool fCanHaveIOBitmap;
795 int rc2 = SELMGetTSSInfo(pVM, pVCpu, &GCPtrTss, &cbTss, &fCanHaveIOBitmap);
796 if (RT_FAILURE(rc2))
797 {
798 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d %Rrc -> #GP(0)\n", Port, cb, rc2));
799 return TRPMRaiseXcptErr(pVCpu, pCtxCore, X86_XCPT_GP, 0);
800 }
801
802 if ( !fCanHaveIOBitmap
803 || cbTss <= sizeof(VBOXTSS)) /** @todo r=bird: Should this really include the interrupt redirection bitmap? */
804 {
805 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d cbTss=%#x fCanHaveIOBitmap=%RTbool -> #GP(0)\n",
806 Port, cb, cbTss, fCanHaveIOBitmap));
807 return TRPMRaiseXcptErr(pVCpu, pCtxCore, X86_XCPT_GP, 0);
808 }
809
810 /*
811 * Fetch the I/O bitmap offset.
812 */
813 uint16_t offIOPB;
814 VBOXSTRICTRC rcStrict = PGMPhysInterpretedRead(pVCpu, pCtxCore, &offIOPB, GCPtrTss + RT_OFFSETOF(VBOXTSS, offIoBitmap), sizeof(offIOPB));
815 if (rcStrict != VINF_SUCCESS)
816 {
817 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d GCPtrTss=%RGv %Rrc\n",
818 Port, cb, GCPtrTss, VBOXSTRICTRC_VAL(rcStrict)));
819 return rcStrict;
820 }
821
822 /*
823 * Check the limit and read the two bitmap bytes.
824 */
825 uint32_t offTss = offIOPB + (Port >> 3);
826 if (offTss + 1 >= cbTss)
827 {
828 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d offTss=%#x cbTss=%#x -> #GP(0)\n",
829 Port, cb, offTss, cbTss));
830 return TRPMRaiseXcptErr(pVCpu, pCtxCore, X86_XCPT_GP, 0);
831 }
832 uint16_t u16;
833 rcStrict = PGMPhysInterpretedRead(pVCpu, pCtxCore, &u16, GCPtrTss + offTss, sizeof(u16));
834 if (rcStrict != VINF_SUCCESS)
835 {
836 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d GCPtrTss=%RGv offTss=%#x -> %Rrc\n",
837 Port, cb, GCPtrTss, offTss, VBOXSTRICTRC_VAL(rcStrict)));
838 return rcStrict;
839 }
840
841 /*
842 * All the bits must be clear.
843 */
844 if ((u16 >> (Port & 7)) & ((1 << cb) - 1))
845 {
846 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d u16=%#x (offTss=%#x) -> #GP(0)\n",
847 Port, cb, u16, offTss));
848 return TRPMRaiseXcptErr(pVCpu, pCtxCore, X86_XCPT_GP, 0);
849 }
850 LogFlow(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d offTss=%#x cbTss=%#x u16=%#x -> OK\n",
851 Port, cb, u16, offTss, cbTss));
852 }
853 return VINF_SUCCESS;
854}
855
856
857/**
858 * Fress an MMIO range after the reference counter has become zero.
859 *
860 * @param pVM The cross context VM structure.
861 * @param pRange The range to free.
862 */
863void iomMmioFreeRange(PVM pVM, PIOMMMIORANGE pRange)
864{
865 MMHyperFree(pVM, pRange);
866}
867
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