VirtualBox

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

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

VMM: ran scm. Mostly svn:keywords changes (adding Revision).

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

© 2023 Oracle
ContactPrivacy policyTerms of Use