VirtualBox

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

最後變更 在這個檔案從45305是 45305,由 vboxsync 提交於 12 年 前

IOM: Adding pVCpu to a lot of calls and moving the lookup caches from VM to VMCPU.

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

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