VirtualBox

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

Last change on this file since 56971 was 56415, checked in by vboxsync, 10 years ago

IOM: Do the single I/O fallback in the string APIs instead of having the caller do it in a non-optimal manner.

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