VirtualBox

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

Last change on this file since 41692 was 41692, checked in by vboxsync, 13 years ago

DIS: Reducing the DISCPUMODE even more (200 bytes now) and making it have the same layout in all contexts. This is useful since it's used several places in the VM structure. Also a bunch of other cleanups.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 35.5 KB
Line 
1/* $Id: IOMAll.cpp 41692 2012-06-13 19:32:54Z 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 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.reg_gen, (uint32_t *)pu64Data);
84 return true;
85 }
86
87 if (pParam->fUse & DISUSE_REG_GEN16)
88 {
89 *pcbSize = 2;
90 DISFetchReg16(pRegFrame, pParam->base.reg_gen, (uint16_t *)pu64Data);
91 return true;
92 }
93
94 if (pParam->fUse & DISUSE_REG_GEN8)
95 {
96 *pcbSize = 1;
97 DISFetchReg8(pRegFrame, pParam->base.reg_gen, (uint8_t *)pu64Data);
98 return true;
99 }
100
101 Assert(pParam->fUse & DISUSE_REG_GEN64);
102 *pcbSize = 8;
103 DISFetchReg64(pRegFrame, pParam->base.reg_gen, pu64Data);
104 return true;
105 }
106 else
107 {
108 if (pParam->fUse & (DISUSE_IMMEDIATE64 | DISUSE_IMMEDIATE64_SX8))
109 {
110 *pcbSize = 8;
111 *pu64Data = pParam->parval;
112 return true;
113 }
114
115 if (pParam->fUse & (DISUSE_IMMEDIATE32 | DISUSE_IMMEDIATE32_SX8))
116 {
117 *pcbSize = 4;
118 *pu64Data = (uint32_t)pParam->parval;
119 return true;
120 }
121
122 if (pParam->fUse & (DISUSE_IMMEDIATE16 | DISUSE_IMMEDIATE16_SX8))
123 {
124 *pcbSize = 2;
125 *pu64Data = (uint16_t)pParam->parval;
126 return true;
127 }
128
129 if (pParam->fUse & DISUSE_IMMEDIATE8)
130 {
131 *pcbSize = 1;
132 *pu64Data = (uint8_t)pParam->parval;
133 return true;
134 }
135
136 if (pParam->fUse & DISUSE_REG_SEG)
137 {
138 *pcbSize = 2;
139 DISFetchRegSeg(pRegFrame, (DIS_SELREG)pParam->base.reg_seg, (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, PCOP_PARAMETER 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.reg_gen, (uint32_t)u64Data);
172 return true;
173 }
174
175 if (pParam->fUse & DISUSE_REG_GEN64)
176 {
177 DISWriteReg64(pRegFrame, pParam->base.reg_gen, u64Data);
178 return true;
179 }
180
181 if (pParam->fUse & DISUSE_REG_GEN16)
182 {
183 DISWriteReg16(pRegFrame, pParam->base.reg_gen, (uint16_t)u64Data);
184 return true;
185 }
186
187 if (pParam->fUse & DISUSE_REG_GEN8)
188 {
189 DISWriteReg8(pRegFrame, pParam->base.reg_gen, (uint8_t)u64Data);
190 return true;
191 }
192
193 if (pParam->fUse & DISUSE_REG_SEG)
194 {
195 DISWriteRegSeg(pRegFrame, (DIS_SELREG)pParam->base.reg_seg, (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 VM handle.
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 VM handle.
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 VM handle.
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 VM handle.
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 VM handle.
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, pCtxCore);
856 uint32_t cpl = CPUMGetGuestCPL(pVCpu, pCtxCore);
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 The VM handle.
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.

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