VirtualBox

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

Last change on this file since 37529 was 37467, checked in by vboxsync, 14 years ago

IOM: Clean up locking now that all devices has its own CS.

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