VirtualBox

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

Last change on this file since 45301 was 45301, checked in by vboxsync, 12 years ago

IOM: Preparing to use read/write critsect.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 35.7 KB
Line 
1/* $Id: IOMAll.cpp 45301 2013-04-03 09:51:13Z vboxsync $ */
2/** @file
3 * IOM - Input / Output Monitor - Any Context.
4 */
5
6/*
7 * Copyright (C) 2006-2012 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 PDMCritSectRwIsWriteOwner(&pVM->iom.s.CritSect);
52#else
53 return PDMCritSectIsOwner(&pVM->iom.s.CritSect);
54#endif
55}
56
57
58/**
59 * Returns the contents of register or immediate data of instruction's parameter.
60 *
61 * @returns true on success.
62 *
63 * @todo Get rid of this code. Use DISQueryParamVal instead
64 *
65 * @param pCpu Pointer to current disassembler context.
66 * @param pParam Pointer to parameter of instruction to process.
67 * @param pRegFrame Pointer to CPUMCTXCORE guest structure.
68 * @param pu64Data Where to store retrieved data.
69 * @param pcbSize Where to store the size of data (1, 2, 4, 8).
70 */
71bool iomGetRegImmData(PDISCPUSTATE pCpu, PCDISOPPARAM pParam, PCPUMCTXCORE pRegFrame, uint64_t *pu64Data, unsigned *pcbSize)
72{
73 NOREF(pCpu);
74 if (pParam->fUse & (DISUSE_BASE | DISUSE_INDEX | DISUSE_SCALE | DISUSE_DISPLACEMENT8 | DISUSE_DISPLACEMENT16 | DISUSE_DISPLACEMENT32))
75 {
76 *pcbSize = 0;
77 *pu64Data = 0;
78 return false;
79 }
80
81 /* divide and conquer */
82 if (pParam->fUse & (DISUSE_REG_GEN64 | DISUSE_REG_GEN32 | DISUSE_REG_GEN16 | DISUSE_REG_GEN8))
83 {
84 if (pParam->fUse & DISUSE_REG_GEN32)
85 {
86 *pcbSize = 4;
87 DISFetchReg32(pRegFrame, pParam->Base.idxGenReg, (uint32_t *)pu64Data);
88 return true;
89 }
90
91 if (pParam->fUse & DISUSE_REG_GEN16)
92 {
93 *pcbSize = 2;
94 DISFetchReg16(pRegFrame, pParam->Base.idxGenReg, (uint16_t *)pu64Data);
95 return true;
96 }
97
98 if (pParam->fUse & DISUSE_REG_GEN8)
99 {
100 *pcbSize = 1;
101 DISFetchReg8(pRegFrame, pParam->Base.idxGenReg, (uint8_t *)pu64Data);
102 return true;
103 }
104
105 Assert(pParam->fUse & DISUSE_REG_GEN64);
106 *pcbSize = 8;
107 DISFetchReg64(pRegFrame, pParam->Base.idxGenReg, pu64Data);
108 return true;
109 }
110 else
111 {
112 if (pParam->fUse & (DISUSE_IMMEDIATE64 | DISUSE_IMMEDIATE64_SX8))
113 {
114 *pcbSize = 8;
115 *pu64Data = pParam->uValue;
116 return true;
117 }
118
119 if (pParam->fUse & (DISUSE_IMMEDIATE32 | DISUSE_IMMEDIATE32_SX8))
120 {
121 *pcbSize = 4;
122 *pu64Data = (uint32_t)pParam->uValue;
123 return true;
124 }
125
126 if (pParam->fUse & (DISUSE_IMMEDIATE16 | DISUSE_IMMEDIATE16_SX8))
127 {
128 *pcbSize = 2;
129 *pu64Data = (uint16_t)pParam->uValue;
130 return true;
131 }
132
133 if (pParam->fUse & DISUSE_IMMEDIATE8)
134 {
135 *pcbSize = 1;
136 *pu64Data = (uint8_t)pParam->uValue;
137 return true;
138 }
139
140 if (pParam->fUse & DISUSE_REG_SEG)
141 {
142 *pcbSize = 2;
143 DISFetchRegSeg(pRegFrame, (DISSELREG)pParam->Base.idxSegReg, (RTSEL *)pu64Data);
144 return true;
145 } /* Else - error. */
146
147 AssertFailed();
148 *pcbSize = 0;
149 *pu64Data = 0;
150 return false;
151 }
152}
153
154
155/**
156 * Saves data to 8/16/32 general purpose or segment register defined by
157 * instruction's parameter.
158 *
159 * @returns true on success.
160 * @param pCpu Pointer to current disassembler context.
161 * @param pParam Pointer to parameter of instruction to process.
162 * @param pRegFrame Pointer to CPUMCTXCORE guest structure.
163 * @param u64Data 8/16/32/64 bit data to store.
164 */
165bool iomSaveDataToReg(PDISCPUSTATE pCpu, PCDISOPPARAM pParam, PCPUMCTXCORE pRegFrame, uint64_t u64Data)
166{
167 NOREF(pCpu);
168 if (pParam->fUse & (DISUSE_BASE | DISUSE_INDEX | DISUSE_SCALE | DISUSE_DISPLACEMENT8 | DISUSE_DISPLACEMENT16 | DISUSE_DISPLACEMENT32 | DISUSE_DISPLACEMENT64 | DISUSE_IMMEDIATE8 | DISUSE_IMMEDIATE16 | DISUSE_IMMEDIATE32 | DISUSE_IMMEDIATE32_SX8 | DISUSE_IMMEDIATE16_SX8))
169 {
170 return false;
171 }
172
173 if (pParam->fUse & DISUSE_REG_GEN32)
174 {
175 DISWriteReg32(pRegFrame, pParam->Base.idxGenReg, (uint32_t)u64Data);
176 return true;
177 }
178
179 if (pParam->fUse & DISUSE_REG_GEN64)
180 {
181 DISWriteReg64(pRegFrame, pParam->Base.idxGenReg, u64Data);
182 return true;
183 }
184
185 if (pParam->fUse & DISUSE_REG_GEN16)
186 {
187 DISWriteReg16(pRegFrame, pParam->Base.idxGenReg, (uint16_t)u64Data);
188 return true;
189 }
190
191 if (pParam->fUse & DISUSE_REG_GEN8)
192 {
193 DISWriteReg8(pRegFrame, pParam->Base.idxGenReg, (uint8_t)u64Data);
194 return true;
195 }
196
197 if (pParam->fUse & DISUSE_REG_SEG)
198 {
199 DISWriteRegSeg(pRegFrame, (DISSELREG)pParam->Base.idxSegReg, (RTSEL)u64Data);
200 return true;
201 }
202
203 /* Else - error. */
204 return false;
205}
206
207
208//#undef LOG_GROUP
209//#define LOG_GROUP LOG_GROUP_IOM_IOPORT
210
211/**
212 * Reads an I/O port register.
213 *
214 * @returns Strict VBox status code. Informational status codes other than the one documented
215 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
216 * @retval VINF_SUCCESS Success.
217 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
218 * status code must be passed on to EM.
219 * @retval VINF_IOM_R3_IOPORT_READ Defer the read to ring-3. (R0/GC only)
220 *
221 * @param pVM Pointer to the VM.
222 * @param Port The port to read.
223 * @param pu32Value Where to store the value read.
224 * @param cbValue The size of the register to read in bytes. 1, 2 or 4 bytes.
225 */
226VMMDECL(VBOXSTRICTRC) IOMIOPortRead(PVM pVM, RTIOPORT Port, uint32_t *pu32Value, size_t cbValue)
227{
228/** @todo should initialize *pu32Value here because it can happen that some
229 * handle is buggy and doesn't handle all cases. */
230 /* Take the IOM lock before performing any device I/O. */
231 int rc2 = IOM_LOCK(pVM);
232#ifndef IN_RING3
233 if (rc2 == VERR_SEM_BUSY)
234 return VINF_IOM_R3_IOPORT_READ;
235#endif
236 AssertRC(rc2);
237#if defined(IEM_VERIFICATION_MODE) && defined(IN_RING3)
238 IEMNotifyIOPortRead(pVM, Port, cbValue);
239#endif
240
241#ifdef VBOX_WITH_STATISTICS
242 /*
243 * Get the statistics record.
244 */
245 PIOMIOPORTSTATS pStats = pVM->iom.s.CTX_SUFF(pStatsLastRead);
246 if (!pStats || pStats->Core.Key != Port)
247 {
248 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTX_SUFF(pTrees)->IOPortStatTree, Port);
249 if (pStats)
250 pVM->iom.s.CTX_SUFF(pStatsLastRead) = pStats;
251 }
252#endif
253
254 /*
255 * Get handler for current context.
256 */
257 CTX_SUFF(PIOMIOPORTRANGE) pRange = pVM->iom.s.CTX_SUFF(pRangeLastRead);
258 if ( !pRange
259 || (unsigned)Port - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
260 {
261 pRange = iomIOPortGetRange(pVM, Port);
262 if (pRange)
263 pVM->iom.s.CTX_SUFF(pRangeLastRead) = pRange;
264 }
265 MMHYPER_RC_ASSERT_RCPTR(pVM, pRange);
266 if (pRange)
267 {
268 /*
269 * Found a range, get the data in case we leave the IOM lock.
270 */
271 PFNIOMIOPORTIN pfnInCallback = pRange->pfnInCallback;
272#ifndef IN_RING3
273 if (!pfnInCallback)
274 {
275 STAM_STATS({ if (pStats) STAM_COUNTER_INC(&pStats->InRZToR3); });
276 IOM_UNLOCK(pVM);
277 return VINF_IOM_R3_IOPORT_READ;
278 }
279#endif
280 void *pvUser = pRange->pvUser;
281 PPDMDEVINS pDevIns = pRange->pDevIns;
282 IOM_UNLOCK(pVM);
283
284 /*
285 * Call the device.
286 */
287 VBOXSTRICTRC rcStrict = PDMCritSectEnter(pDevIns->CTX_SUFF(pCritSectRo), VINF_IOM_R3_IOPORT_READ);
288 if (rcStrict != VINF_SUCCESS)
289 {
290 STAM_STATS({ if (pStats) STAM_COUNTER_INC(&pStats->InRZToR3); });
291 return rcStrict;
292 }
293#ifdef VBOX_WITH_STATISTICS
294 if (pStats)
295 {
296 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfIn), a);
297 rcStrict = pfnInCallback(pDevIns, pvUser, Port, pu32Value, (unsigned)cbValue);
298 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfIn), a);
299 }
300 else
301#endif
302 rcStrict = pfnInCallback(pDevIns, pvUser, Port, pu32Value, (unsigned)cbValue);
303 PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo));
304
305#ifdef VBOX_WITH_STATISTICS
306 if (rcStrict == VINF_SUCCESS && pStats)
307 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(In));
308# ifndef IN_RING3
309 else if (rcStrict == VINF_IOM_R3_IOPORT_READ && pStats)
310 STAM_COUNTER_INC(&pStats->InRZToR3);
311# endif
312#endif
313 if (rcStrict == VERR_IOM_IOPORT_UNUSED)
314 {
315 /* make return value */
316 rcStrict = VINF_SUCCESS;
317 switch (cbValue)
318 {
319 case 1: *(uint8_t *)pu32Value = 0xff; break;
320 case 2: *(uint16_t *)pu32Value = 0xffff; break;
321 case 4: *(uint32_t *)pu32Value = UINT32_C(0xffffffff); break;
322 default:
323 AssertMsgFailed(("Invalid I/O port size %d. Port=%d\n", cbValue, Port));
324 return VERR_IOM_INVALID_IOPORT_SIZE;
325 }
326 }
327 Log3(("IOMIOPortRead: Port=%RTiop *pu32=%08RX32 cb=%d rc=%Rrc\n", Port, *pu32Value, cbValue, VBOXSTRICTRC_VAL(rcStrict)));
328 return rcStrict;
329 }
330
331#ifndef IN_RING3
332 /*
333 * Handler in ring-3?
334 */
335 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeR3(pVM, Port);
336 if (pRangeR3)
337 {
338# ifdef VBOX_WITH_STATISTICS
339 if (pStats)
340 STAM_COUNTER_INC(&pStats->InRZToR3);
341# endif
342 IOM_UNLOCK(pVM);
343 return VINF_IOM_R3_IOPORT_READ;
344 }
345#endif
346
347 /*
348 * Ok, no handler for this port.
349 */
350#ifdef VBOX_WITH_STATISTICS
351 if (pStats)
352 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(In));
353 else
354 {
355# ifndef IN_RING3
356 /* Ring-3 will have to create the statistics record. */
357 IOM_UNLOCK(pVM);
358 return VINF_IOM_R3_IOPORT_READ;
359# else
360 pStats = iomR3IOPortStatsCreate(pVM, Port, NULL);
361 if (pStats)
362 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(In));
363# endif
364 }
365#endif
366
367 /* make return value */
368 switch (cbValue)
369 {
370 case 1: *(uint8_t *)pu32Value = 0xff; break;
371 case 2: *(uint16_t *)pu32Value = 0xffff; break;
372 case 4: *(uint32_t *)pu32Value = UINT32_C(0xffffffff); break;
373 default:
374 AssertMsgFailed(("Invalid I/O port size %d. Port=%d\n", cbValue, Port));
375 IOM_UNLOCK(pVM);
376 return VERR_IOM_INVALID_IOPORT_SIZE;
377 }
378 Log3(("IOMIOPortRead: Port=%RTiop *pu32=%08RX32 cb=%d rc=VINF_SUCCESS\n", Port, *pu32Value, cbValue));
379 IOM_UNLOCK(pVM);
380 return VINF_SUCCESS;
381}
382
383
384/**
385 * Reads the string buffer of an I/O port register.
386 *
387 * @returns Strict VBox status code. Informational status codes other than the one documented
388 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
389 * @retval VINF_SUCCESS Success.
390 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
391 * status code must be passed on to EM.
392 * @retval VINF_IOM_R3_IOPORT_READ Defer the read to ring-3. (R0/GC only)
393 *
394 * @param pVM Pointer to the VM.
395 * @param Port The port to read.
396 * @param pGCPtrDst Pointer to the destination buffer (GC, incremented appropriately).
397 * @param pcTransfers Pointer to the number of transfer units to read, on return remaining transfer units.
398 * @param cb Size of the transfer unit (1, 2 or 4 bytes).
399 */
400VMMDECL(VBOXSTRICTRC) IOMIOPortReadString(PVM pVM, RTIOPORT Port, PRTGCPTR pGCPtrDst, PRTGCUINTREG pcTransfers, unsigned cb)
401{
402 /* Take the IOM lock before performing any device I/O. */
403 int rc2 = IOM_LOCK(pVM);
404#ifndef IN_RING3
405 if (rc2 == VERR_SEM_BUSY)
406 return VINF_IOM_R3_IOPORT_READ;
407#endif
408 AssertRC(rc2);
409#if defined(IEM_VERIFICATION_MODE) && defined(IN_RING3)
410 IEMNotifyIOPortReadString(pVM, Port, *pGCPtrDst, *pcTransfers, cb);
411#endif
412
413#ifdef LOG_ENABLED
414 const RTGCUINTREG cTransfers = *pcTransfers;
415#endif
416#ifdef VBOX_WITH_STATISTICS
417 /*
418 * Get the statistics record.
419 */
420 PIOMIOPORTSTATS pStats = pVM->iom.s.CTX_SUFF(pStatsLastRead);
421 if (!pStats || pStats->Core.Key != Port)
422 {
423 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTX_SUFF(pTrees)->IOPortStatTree, Port);
424 if (pStats)
425 pVM->iom.s.CTX_SUFF(pStatsLastRead) = pStats;
426 }
427#endif
428
429 /*
430 * Get handler for current context.
431 */
432 CTX_SUFF(PIOMIOPORTRANGE) pRange = pVM->iom.s.CTX_SUFF(pRangeLastRead);
433 if ( !pRange
434 || (unsigned)Port - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
435 {
436 pRange = iomIOPortGetRange(pVM, Port);
437 if (pRange)
438 pVM->iom.s.CTX_SUFF(pRangeLastRead) = pRange;
439 }
440 MMHYPER_RC_ASSERT_RCPTR(pVM, pRange);
441 if (pRange)
442 {
443 /*
444 * Found a range.
445 */
446 PFNIOMIOPORTINSTRING pfnInStrCallback = pRange->pfnInStrCallback;
447#ifndef IN_RING3
448 if (!pfnInStrCallback)
449 {
450 STAM_STATS({ if (pStats) STAM_COUNTER_INC(&pStats->InRZToR3); });
451 IOM_UNLOCK(pVM);
452 return VINF_IOM_R3_IOPORT_READ;
453 }
454#endif
455 void *pvUser = pRange->pvUser;
456 PPDMDEVINS pDevIns = pRange->pDevIns;
457 IOM_UNLOCK(pVM);
458
459 /*
460 * Call the device.
461 */
462 VBOXSTRICTRC rcStrict = PDMCritSectEnter(pDevIns->CTX_SUFF(pCritSectRo), VINF_IOM_R3_IOPORT_READ);
463 if (rcStrict != VINF_SUCCESS)
464 {
465 STAM_STATS({ if (pStats) STAM_COUNTER_INC(&pStats->InRZToR3); });
466 return rcStrict;
467 }
468#ifdef VBOX_WITH_STATISTICS
469 if (pStats)
470 {
471 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfIn), a);
472 rcStrict = pfnInStrCallback(pDevIns, pvUser, Port, pGCPtrDst, pcTransfers, cb);
473 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfIn), a);
474 }
475 else
476#endif
477 rcStrict = pfnInStrCallback(pDevIns, pvUser, Port, pGCPtrDst, pcTransfers, cb);
478 PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo));
479
480#ifdef VBOX_WITH_STATISTICS
481 if (rcStrict == VINF_SUCCESS && pStats)
482 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(In));
483# ifndef IN_RING3
484 else if (rcStrict == VINF_IOM_R3_IOPORT_READ && pStats)
485 STAM_COUNTER_INC(&pStats->InRZToR3);
486# endif
487#endif
488 Log3(("IOMIOPortReadStr: Port=%RTiop pGCPtrDst=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=%Rrc\n",
489 Port, pGCPtrDst, pcTransfers, cTransfers, *pcTransfers, cb, VBOXSTRICTRC_VAL(rcStrict)));
490 return rcStrict;
491 }
492
493#ifndef IN_RING3
494 /*
495 * Handler in ring-3?
496 */
497 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeR3(pVM, Port);
498 if (pRangeR3)
499 {
500# ifdef VBOX_WITH_STATISTICS
501 if (pStats)
502 STAM_COUNTER_INC(&pStats->InRZToR3);
503# endif
504 IOM_UNLOCK(pVM);
505 return VINF_IOM_R3_IOPORT_READ;
506 }
507#endif
508
509 /*
510 * Ok, no handler for this port.
511 */
512#ifdef VBOX_WITH_STATISTICS
513 if (pStats)
514 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(In));
515 else
516 {
517# ifndef IN_RING3
518 /* Ring-3 will have to create the statistics record. */
519 IOM_UNLOCK(pVM);
520 return VINF_IOM_R3_IOPORT_READ;
521# else
522 pStats = iomR3IOPortStatsCreate(pVM, Port, NULL);
523 if (pStats)
524 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(In));
525# endif
526 }
527#endif
528
529 Log3(("IOMIOPortReadStr: Port=%RTiop pGCPtrDst=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=VINF_SUCCESS\n",
530 Port, pGCPtrDst, pcTransfers, cTransfers, *pcTransfers, cb));
531 IOM_UNLOCK(pVM);
532 return VINF_SUCCESS;
533}
534
535
536/**
537 * Writes to an I/O port register.
538 *
539 * @returns Strict VBox status code. Informational status codes other than the one documented
540 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
541 * @retval VINF_SUCCESS Success.
542 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
543 * status code must be passed on to EM.
544 * @retval VINF_IOM_R3_IOPORT_WRITE Defer the write to ring-3. (R0/GC only)
545 *
546 * @param pVM Pointer to the VM.
547 * @param Port The port to write to.
548 * @param u32Value The value to write.
549 * @param cbValue The size of the register to read in bytes. 1, 2 or 4 bytes.
550 */
551VMMDECL(VBOXSTRICTRC) IOMIOPortWrite(PVM pVM, RTIOPORT Port, uint32_t u32Value, size_t cbValue)
552{
553 /* Take the IOM lock before performing any device I/O. */
554 int rc2 = IOM_LOCK(pVM);
555#ifndef IN_RING3
556 if (rc2 == VERR_SEM_BUSY)
557 return VINF_IOM_R3_IOPORT_WRITE;
558#endif
559 AssertRC(rc2);
560#if defined(IEM_VERIFICATION_MODE) && defined(IN_RING3)
561 IEMNotifyIOPortWrite(pVM, Port, u32Value, cbValue);
562#endif
563
564/** @todo bird: When I get time, I'll remove the RC/R0 trees and link the RC/R0
565 * entries to the ring-3 node. */
566#ifdef VBOX_WITH_STATISTICS
567 /*
568 * Find the statistics record.
569 */
570 PIOMIOPORTSTATS pStats = pVM->iom.s.CTX_SUFF(pStatsLastWrite);
571 if (!pStats || pStats->Core.Key != Port)
572 {
573 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTX_SUFF(pTrees)->IOPortStatTree, Port);
574 if (pStats)
575 pVM->iom.s.CTX_SUFF(pStatsLastWrite) = pStats;
576 }
577#endif
578
579 /*
580 * Get handler for current context.
581 */
582 CTX_SUFF(PIOMIOPORTRANGE) pRange = pVM->iom.s.CTX_SUFF(pRangeLastWrite);
583 if ( !pRange
584 || (unsigned)Port - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
585 {
586 pRange = iomIOPortGetRange(pVM, Port);
587 if (pRange)
588 pVM->iom.s.CTX_SUFF(pRangeLastWrite) = pRange;
589 }
590 MMHYPER_RC_ASSERT_RCPTR(pVM, pRange);
591 if (pRange)
592 {
593 /*
594 * Found a range.
595 */
596 PFNIOMIOPORTOUT pfnOutCallback = pRange->pfnOutCallback;
597#ifndef IN_RING3
598 if (!pfnOutCallback)
599 {
600 STAM_STATS({ if (pStats) STAM_COUNTER_INC(&pStats->OutRZToR3); });
601 IOM_UNLOCK(pVM);
602 return VINF_IOM_R3_IOPORT_WRITE;
603 }
604#endif
605 void *pvUser = pRange->pvUser;
606 PPDMDEVINS pDevIns = pRange->pDevIns;
607 IOM_UNLOCK(pVM);
608
609 /*
610 * Call the device.
611 */
612 VBOXSTRICTRC rcStrict = PDMCritSectEnter(pDevIns->CTX_SUFF(pCritSectRo), VINF_IOM_R3_IOPORT_WRITE);
613 if (rcStrict != VINF_SUCCESS)
614 {
615 STAM_STATS({ if (pStats) STAM_COUNTER_INC(&pStats->OutRZToR3); });
616 return rcStrict;
617 }
618#ifdef VBOX_WITH_STATISTICS
619 if (pStats)
620 {
621 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfOut), a);
622 rcStrict = pfnOutCallback(pDevIns, pvUser, Port, u32Value, (unsigned)cbValue);
623 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfOut), a);
624 }
625 else
626#endif
627 rcStrict = pfnOutCallback(pDevIns, pvUser, Port, u32Value, (unsigned)cbValue);
628 PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo));
629
630#ifdef VBOX_WITH_STATISTICS
631 if (rcStrict == VINF_SUCCESS && pStats)
632 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Out));
633# ifndef IN_RING3
634 else if (rcStrict == VINF_IOM_R3_IOPORT_WRITE && pStats)
635 STAM_COUNTER_INC(&pStats->OutRZToR3);
636# endif
637#endif
638 Log3(("IOMIOPortWrite: Port=%RTiop u32=%08RX32 cb=%d rc=%Rrc\n", Port, u32Value, cbValue, VBOXSTRICTRC_VAL(rcStrict)));
639 return rcStrict;
640 }
641
642#ifndef IN_RING3
643 /*
644 * Handler in ring-3?
645 */
646 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeR3(pVM, Port);
647 if (pRangeR3)
648 {
649# ifdef VBOX_WITH_STATISTICS
650 if (pStats)
651 STAM_COUNTER_INC(&pStats->OutRZToR3);
652# endif
653 IOM_UNLOCK(pVM);
654 return VINF_IOM_R3_IOPORT_WRITE;
655 }
656#endif
657
658 /*
659 * Ok, no handler for that port.
660 */
661#ifdef VBOX_WITH_STATISTICS
662 /* statistics. */
663 if (pStats)
664 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Out));
665 else
666 {
667# ifndef IN_RING3
668 /* R3 will have to create the statistics record. */
669 IOM_UNLOCK(pVM);
670 return VINF_IOM_R3_IOPORT_WRITE;
671# else
672 pStats = iomR3IOPortStatsCreate(pVM, Port, NULL);
673 if (pStats)
674 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Out));
675# endif
676 }
677#endif
678 Log3(("IOMIOPortWrite: Port=%RTiop u32=%08RX32 cb=%d nop\n", Port, u32Value, cbValue));
679 IOM_UNLOCK(pVM);
680 return VINF_SUCCESS;
681}
682
683
684/**
685 * Writes the string buffer of an I/O port register.
686 *
687 * @returns Strict VBox status code. Informational status codes other than the one documented
688 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
689 * @retval VINF_SUCCESS Success.
690 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
691 * status code must be passed on to EM.
692 * @retval VINF_IOM_R3_IOPORT_WRITE Defer the write to ring-3. (R0/GC only)
693 *
694 * @param pVM Pointer to the VM.
695 * @param Port The port to write.
696 * @param pGCPtrSrc Pointer to the source buffer (GC, incremented appropriately).
697 * @param pcTransfers Pointer to the number of transfer units to write, on return remaining transfer units.
698 * @param cb Size of the transfer unit (1, 2 or 4 bytes).
699 * */
700VMMDECL(VBOXSTRICTRC) IOMIOPortWriteString(PVM pVM, RTIOPORT Port, PRTGCPTR pGCPtrSrc, PRTGCUINTREG pcTransfers, unsigned cb)
701{
702 /* Take the IOM lock before performing any device I/O. */
703 int rc2 = IOM_LOCK(pVM);
704#ifndef IN_RING3
705 if (rc2 == VERR_SEM_BUSY)
706 return VINF_IOM_R3_IOPORT_WRITE;
707#endif
708 AssertRC(rc2);
709#if defined(IEM_VERIFICATION_MODE) && defined(IN_RING3)
710 IEMNotifyIOPortWriteString(pVM, Port, *pGCPtrSrc, *pcTransfers, cb);
711#endif
712
713#ifdef LOG_ENABLED
714 const RTGCUINTREG cTransfers = *pcTransfers;
715#endif
716#ifdef VBOX_WITH_STATISTICS
717 /*
718 * Get the statistics record.
719 */
720 PIOMIOPORTSTATS pStats = pVM->iom.s.CTX_SUFF(pStatsLastWrite);
721 if (!pStats || pStats->Core.Key != Port)
722 {
723 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTX_SUFF(pTrees)->IOPortStatTree, Port);
724 if (pStats)
725 pVM->iom.s.CTX_SUFF(pStatsLastWrite) = pStats;
726 }
727#endif
728
729 /*
730 * Get handler for current context.
731 */
732 CTX_SUFF(PIOMIOPORTRANGE) pRange = pVM->iom.s.CTX_SUFF(pRangeLastWrite);
733 if ( !pRange
734 || (unsigned)Port - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
735 {
736 pRange = iomIOPortGetRange(pVM, Port);
737 if (pRange)
738 pVM->iom.s.CTX_SUFF(pRangeLastWrite) = pRange;
739 }
740 MMHYPER_RC_ASSERT_RCPTR(pVM, pRange);
741 if (pRange)
742 {
743 /*
744 * Found a range.
745 */
746 PFNIOMIOPORTOUTSTRING pfnOutStrCallback = pRange->pfnOutStrCallback;
747#ifndef IN_RING3
748 if (!pfnOutStrCallback)
749 {
750 STAM_STATS({ if (pStats) STAM_COUNTER_INC(&pStats->OutRZToR3); });
751 IOM_UNLOCK(pVM);
752 return VINF_IOM_R3_IOPORT_WRITE;
753 }
754#endif
755 void *pvUser = pRange->pvUser;
756 PPDMDEVINS pDevIns = pRange->pDevIns;
757 IOM_UNLOCK(pVM);
758
759 /*
760 * Call the device.
761 */
762 VBOXSTRICTRC rcStrict = PDMCritSectEnter(pDevIns->CTX_SUFF(pCritSectRo), VINF_IOM_R3_IOPORT_WRITE);
763 if (rcStrict != VINF_SUCCESS)
764 {
765 STAM_STATS({ if (pStats) STAM_COUNTER_INC(&pStats->OutRZToR3); });
766 return rcStrict;
767 }
768#ifdef VBOX_WITH_STATISTICS
769 if (pStats)
770 {
771 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfOut), a);
772 rcStrict = pfnOutStrCallback(pDevIns, pvUser, Port, pGCPtrSrc, pcTransfers, cb);
773 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfOut), a);
774 }
775 else
776#endif
777 rcStrict = pfnOutStrCallback(pDevIns, pvUser, Port, pGCPtrSrc, pcTransfers, cb);
778 PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo));
779
780#ifdef VBOX_WITH_STATISTICS
781 if (rcStrict == VINF_SUCCESS && pStats)
782 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Out));
783# ifndef IN_RING3
784 else if (rcStrict == VINF_IOM_R3_IOPORT_WRITE && pStats)
785 STAM_COUNTER_INC(&pStats->OutRZToR3);
786# endif
787#endif
788 Log3(("IOMIOPortWriteStr: Port=%RTiop pGCPtrSrc=%p pcTransfer=%p:{%#x->%#x} cb=%d rcStrict=%Rrc\n",
789 Port, pGCPtrSrc, pcTransfers, cTransfers, *pcTransfers, cb, VBOXSTRICTRC_VAL(rcStrict)));
790 return rcStrict;
791 }
792
793#ifndef IN_RING3
794 /*
795 * Handler in ring-3?
796 */
797 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeR3(pVM, Port);
798 if (pRangeR3)
799 {
800# ifdef VBOX_WITH_STATISTICS
801 if (pStats)
802 STAM_COUNTER_INC(&pStats->OutRZToR3);
803# endif
804 IOM_UNLOCK(pVM);
805 return VINF_IOM_R3_IOPORT_WRITE;
806 }
807#endif
808
809 /*
810 * Ok, no handler for this port.
811 */
812#ifdef VBOX_WITH_STATISTICS
813 if (pStats)
814 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Out));
815 else
816 {
817# ifndef IN_RING3
818 /* Ring-3 will have to create the statistics record. */
819 IOM_UNLOCK(pVM);
820 return VINF_IOM_R3_IOPORT_WRITE;
821# else
822 pStats = iomR3IOPortStatsCreate(pVM, Port, NULL);
823 if (pStats)
824 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Out));
825# endif
826 }
827#endif
828
829 Log3(("IOMIOPortWriteStr: Port=%RTiop pGCPtrSrc=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=VINF_SUCCESS\n",
830 Port, pGCPtrSrc, pcTransfers, cTransfers, *pcTransfers, cb));
831 IOM_UNLOCK(pVM);
832 return VINF_SUCCESS;
833}
834
835
836/**
837 * Checks that the operation is allowed according to the IOPL
838 * level and I/O bitmap.
839 *
840 * @returns Strict VBox status code. Informational status codes other than the one documented
841 * here are to be treated as internal failure.
842 * @retval VINF_SUCCESS Success.
843 * @retval VINF_EM_RAW_GUEST_TRAP The exception was left pending. (TRPMRaiseXcptErr)
844 * @retval VINF_TRPM_XCPT_DISPATCHED The exception was raised and dispatched for raw-mode execution. (TRPMRaiseXcptErr)
845 * @retval VINF_EM_RESCHEDULE_REM The exception was dispatched and cannot be executed in raw-mode. (TRPMRaiseXcptErr)
846 *
847 * @param pVM Pointer to the VM.
848 * @param pCtxCore Pointer to register frame.
849 * @param Port The I/O port number.
850 * @param cb The access size.
851 */
852VMMDECL(VBOXSTRICTRC) IOMInterpretCheckPortIOAccess(PVM pVM, PCPUMCTXCORE pCtxCore, RTIOPORT Port, unsigned cb)
853{
854 PVMCPU pVCpu = VMMGetCpu(pVM);
855
856 /*
857 * If this isn't ring-0, we have to check for I/O privileges.
858 */
859 uint32_t efl = CPUMRawGetEFlags(pVCpu);
860 uint32_t cpl = CPUMGetGuestCPL(pVCpu);
861
862 if ( ( cpl > 0
863 && X86_EFL_GET_IOPL(efl) < cpl)
864 || pCtxCore->eflags.Bits.u1VM /* IOPL is ignored in V86 mode; always check TSS bitmap */
865 )
866 {
867 /*
868 * Get TSS location and check if there can be a I/O bitmap.
869 */
870 RTGCUINTPTR GCPtrTss;
871 RTGCUINTPTR cbTss;
872 bool fCanHaveIOBitmap;
873 int rc2 = SELMGetTSSInfo(pVM, pVCpu, &GCPtrTss, &cbTss, &fCanHaveIOBitmap);
874 if (RT_FAILURE(rc2))
875 {
876 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d %Rrc -> #GP(0)\n", Port, cb, rc2));
877 return TRPMRaiseXcptErr(pVCpu, pCtxCore, X86_XCPT_GP, 0);
878 }
879
880 if ( !fCanHaveIOBitmap
881 || cbTss <= sizeof(VBOXTSS)) /** @todo r=bird: Should this really include the interrupt redirection bitmap? */
882 {
883 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d cbTss=%#x fCanHaveIOBitmap=%RTbool -> #GP(0)\n",
884 Port, cb, cbTss, fCanHaveIOBitmap));
885 return TRPMRaiseXcptErr(pVCpu, pCtxCore, X86_XCPT_GP, 0);
886 }
887
888 /*
889 * Fetch the I/O bitmap offset.
890 */
891 uint16_t offIOPB;
892 VBOXSTRICTRC rcStrict = PGMPhysInterpretedRead(pVCpu, pCtxCore, &offIOPB, GCPtrTss + RT_OFFSETOF(VBOXTSS, offIoBitmap), sizeof(offIOPB));
893 if (rcStrict != VINF_SUCCESS)
894 {
895 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d GCPtrTss=%RGv %Rrc\n",
896 Port, cb, GCPtrTss, VBOXSTRICTRC_VAL(rcStrict)));
897 return rcStrict;
898 }
899
900 /*
901 * Check the limit and read the two bitmap bytes.
902 */
903 uint32_t offTss = offIOPB + (Port >> 3);
904 if (offTss + 1 >= cbTss)
905 {
906 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d offTss=%#x cbTss=%#x -> #GP(0)\n",
907 Port, cb, offTss, cbTss));
908 return TRPMRaiseXcptErr(pVCpu, pCtxCore, X86_XCPT_GP, 0);
909 }
910 uint16_t u16;
911 rcStrict = PGMPhysInterpretedRead(pVCpu, pCtxCore, &u16, GCPtrTss + offTss, sizeof(u16));
912 if (rcStrict != VINF_SUCCESS)
913 {
914 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d GCPtrTss=%RGv offTss=%#x -> %Rrc\n",
915 Port, cb, GCPtrTss, offTss, VBOXSTRICTRC_VAL(rcStrict)));
916 return rcStrict;
917 }
918
919 /*
920 * All the bits must be clear.
921 */
922 if ((u16 >> (Port & 7)) & ((1 << cb) - 1))
923 {
924 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d u16=%#x -> #GP(0)\n",
925 Port, cb, u16, offTss));
926 return TRPMRaiseXcptErr(pVCpu, pCtxCore, X86_XCPT_GP, 0);
927 }
928 LogFlow(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d offTss=%#x cbTss=%#x u16=%#x -> OK\n",
929 Port, cb, u16, offTss, cbTss));
930 }
931 return VINF_SUCCESS;
932}
933
934
935/**
936 * IN <AL|AX|EAX>, <DX|imm16>
937 *
938 * @returns Strict VBox status code. Informational status codes other than the one documented
939 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
940 * @retval VINF_SUCCESS Success.
941 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
942 * status code must be passed on to EM.
943 * @retval VINF_IOM_R3_IOPORT_READ Defer the read to ring-3. (R0/GC only)
944 * @retval VINF_EM_RAW_GUEST_TRAP The exception was left pending. (TRPMRaiseXcptErr)
945 * @retval VINF_TRPM_XCPT_DISPATCHED The exception was raised and dispatched for raw-mode execution. (TRPMRaiseXcptErr)
946 * @retval VINF_EM_RESCHEDULE_REM The exception was dispatched and cannot be executed in raw-mode. (TRPMRaiseXcptErr)
947 *
948 * @param pVM The virtual machine (GC pointer of course).
949 * @param pRegFrame Pointer to CPUMCTXCORE guest registers structure.
950 * @param pCpu Disassembler CPU state.
951 */
952VMMDECL(VBOXSTRICTRC) IOMInterpretIN(PVM pVM, PCPUMCTXCORE pRegFrame, PDISCPUSTATE pCpu)
953{
954#ifdef IN_RC
955 STAM_COUNTER_INC(&pVM->iom.s.StatInstIn);
956#endif
957
958 /*
959 * Get port number from second parameter.
960 * And get the register size from the first parameter.
961 */
962 uint64_t uPort = 0;
963 unsigned cbSize = 0;
964 bool fRc = iomGetRegImmData(pCpu, &pCpu->Param2, pRegFrame, &uPort, &cbSize);
965 AssertMsg(fRc, ("Failed to get reg/imm port number!\n")); NOREF(fRc);
966
967 cbSize = DISGetParamSize(pCpu, &pCpu->Param1);
968 Assert(cbSize > 0);
969 VBOXSTRICTRC rcStrict = IOMInterpretCheckPortIOAccess(pVM, pRegFrame, uPort, cbSize);
970 if (rcStrict == VINF_SUCCESS)
971 {
972 /*
973 * Attempt to read the port.
974 */
975 uint32_t u32Data = UINT32_C(0xffffffff);
976 rcStrict = IOMIOPortRead(pVM, uPort, &u32Data, cbSize);
977 if (IOM_SUCCESS(rcStrict))
978 {
979 /*
980 * Store the result in the AL|AX|EAX register.
981 */
982 fRc = iomSaveDataToReg(pCpu, &pCpu->Param1, pRegFrame, u32Data);
983 AssertMsg(fRc, ("Failed to store register value!\n")); NOREF(fRc);
984 }
985 else
986 AssertMsg(rcStrict == VINF_IOM_R3_IOPORT_READ || RT_FAILURE(rcStrict), ("%Rrc\n", VBOXSTRICTRC_VAL(rcStrict)));
987 }
988 else
989 AssertMsg(rcStrict == VINF_EM_RAW_GUEST_TRAP || rcStrict == VINF_TRPM_XCPT_DISPATCHED || rcStrict == VINF_TRPM_XCPT_DISPATCHED || RT_FAILURE(rcStrict), ("%Rrc\n", VBOXSTRICTRC_VAL(rcStrict)));
990
991 return rcStrict;
992}
993
994
995/**
996 * OUT <DX|imm16>, <AL|AX|EAX>
997 *
998 * @returns Strict VBox status code. Informational status codes other than the one documented
999 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
1000 * @retval VINF_SUCCESS Success.
1001 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
1002 * status code must be passed on to EM.
1003 * @retval VINF_IOM_R3_IOPORT_WRITE Defer the write to ring-3. (R0/GC only)
1004 * @retval VINF_EM_RAW_GUEST_TRAP The exception was left pending. (TRPMRaiseXcptErr)
1005 * @retval VINF_TRPM_XCPT_DISPATCHED The exception was raised and dispatched for raw-mode execution. (TRPMRaiseXcptErr)
1006 * @retval VINF_EM_RESCHEDULE_REM The exception was dispatched and cannot be executed in raw-mode. (TRPMRaiseXcptErr)
1007 *
1008 * @param pVM The virtual machine (GC pointer of course).
1009 * @param pRegFrame Pointer to CPUMCTXCORE guest registers structure.
1010 * @param pCpu Disassembler CPU state.
1011 */
1012VMMDECL(VBOXSTRICTRC) IOMInterpretOUT(PVM pVM, PCPUMCTXCORE pRegFrame, PDISCPUSTATE pCpu)
1013{
1014#ifdef IN_RC
1015 STAM_COUNTER_INC(&pVM->iom.s.StatInstOut);
1016#endif
1017
1018 /*
1019 * Get port number from first parameter.
1020 * And get the register size and value from the second parameter.
1021 */
1022 uint64_t uPort = 0;
1023 unsigned cbSize = 0;
1024 bool fRc = iomGetRegImmData(pCpu, &pCpu->Param1, pRegFrame, &uPort, &cbSize);
1025 AssertMsg(fRc, ("Failed to get reg/imm port number!\n")); NOREF(fRc);
1026
1027 VBOXSTRICTRC rcStrict = IOMInterpretCheckPortIOAccess(pVM, pRegFrame, uPort, cbSize);
1028 if (rcStrict == VINF_SUCCESS)
1029 {
1030 uint64_t u64Data = 0;
1031 fRc = iomGetRegImmData(pCpu, &pCpu->Param2, pRegFrame, &u64Data, &cbSize);
1032 AssertMsg(fRc, ("Failed to get reg value!\n")); NOREF(fRc);
1033
1034 /*
1035 * Attempt to write to the port.
1036 */
1037 rcStrict = IOMIOPortWrite(pVM, uPort, u64Data, cbSize);
1038 AssertMsg(rcStrict == VINF_SUCCESS || rcStrict == VINF_IOM_R3_IOPORT_WRITE || (rcStrict >= VINF_EM_FIRST && rcStrict <= VINF_EM_LAST) || RT_FAILURE(rcStrict), ("%Rrc\n", VBOXSTRICTRC_VAL(rcStrict)));
1039 }
1040 else
1041 AssertMsg(rcStrict == VINF_EM_RAW_GUEST_TRAP || rcStrict == VINF_TRPM_XCPT_DISPATCHED || rcStrict == VINF_TRPM_XCPT_DISPATCHED || RT_FAILURE(rcStrict), ("%Rrc\n", VBOXSTRICTRC_VAL(rcStrict)));
1042 return rcStrict;
1043}
1044
1045
1046/**
1047 * Fress an MMIO range after the reference counter has become zero.
1048 *
1049 * @param pVM Pointer to the VM.
1050 * @param pRange The range to free.
1051 */
1052void iomMmioFreeRange(PVM pVM, PIOMMMIORANGE pRange)
1053{
1054 MMHyperFree(pVM, pRange);
1055}
1056
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