VirtualBox

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

Last change on this file since 13072 was 12989, checked in by vboxsync, 16 years ago

VMM + VBox/cdefs.h: consolidated all the XYZ*DECLS of the VMM into VMM*DECL. Removed dead DECL and IN_XYZ* macros.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 30.1 KB
Line 
1/* $Id: IOMAll.cpp 12989 2008-10-06 02:15:39Z vboxsync $ */
2/** @file
3 * IOM - Input / Output Monitor - Any Context.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22/*******************************************************************************
23* Header Files *
24*******************************************************************************/
25#define LOG_GROUP LOG_GROUP_IOM
26#include <VBox/iom.h>
27#include <VBox/mm.h>
28#include <VBox/param.h>
29#include "IOMInternal.h"
30#include <VBox/vm.h>
31#include <VBox/selm.h>
32#include <VBox/trpm.h>
33#include <VBox/pgm.h>
34#include <VBox/cpum.h>
35#include <VBox/err.h>
36#include <VBox/log.h>
37#include <iprt/assert.h>
38
39
40
41/**
42 * Returns the contents of register or immediate data of instruction's parameter.
43 *
44 * @returns true on success.
45 *
46 * @todo Get rid of this code. Use DISQueryParamVal instead
47 *
48 * @param pCpu Pointer to current disassembler context.
49 * @param pParam Pointer to parameter of instruction to proccess.
50 * @param pRegFrame Pointer to CPUMCTXCORE guest structure.
51 * @param pu64Data Where to store retrieved data.
52 * @param pcbSize Where to store the size of data (1, 2, 4, 8).
53 */
54bool iomGetRegImmData(PDISCPUSTATE pCpu, PCOP_PARAMETER pParam, PCPUMCTXCORE pRegFrame, uint64_t *pu64Data, unsigned *pcbSize)
55{
56 if (pParam->flags & (USE_BASE | USE_INDEX | USE_SCALE | USE_DISPLACEMENT8 | USE_DISPLACEMENT16 | USE_DISPLACEMENT32))
57 {
58 *pcbSize = 0;
59 *pu64Data = 0;
60 return false;
61 }
62
63 /* divide and conquer */
64 if (pParam->flags & (USE_REG_GEN64 | USE_REG_GEN32 | USE_REG_GEN16 | USE_REG_GEN8))
65 {
66 if (pParam->flags & USE_REG_GEN32)
67 {
68 *pcbSize = 4;
69 DISFetchReg32(pRegFrame, pParam->base.reg_gen, (uint32_t *)pu64Data);
70 return true;
71 }
72
73 if (pParam->flags & USE_REG_GEN16)
74 {
75 *pcbSize = 2;
76 DISFetchReg16(pRegFrame, pParam->base.reg_gen, (uint16_t *)pu64Data);
77 return true;
78 }
79
80 if (pParam->flags & USE_REG_GEN8)
81 {
82 *pcbSize = 1;
83 DISFetchReg8(pRegFrame, pParam->base.reg_gen, (uint8_t *)pu64Data);
84 return true;
85 }
86
87 Assert(pParam->flags & USE_REG_GEN64);
88 *pcbSize = 8;
89 DISFetchReg64(pRegFrame, pParam->base.reg_gen, pu64Data);
90 return true;
91 }
92 else
93 {
94 if (pParam->flags & (USE_IMMEDIATE64 | USE_IMMEDIATE64_SX8))
95 {
96 *pcbSize = 8;
97 *pu64Data = pParam->parval;
98 return true;
99 }
100
101 if (pParam->flags & (USE_IMMEDIATE32 | USE_IMMEDIATE32_SX8))
102 {
103 *pcbSize = 4;
104 *pu64Data = (uint32_t)pParam->parval;
105 return true;
106 }
107
108 if (pParam->flags & (USE_IMMEDIATE16 | USE_IMMEDIATE16_SX8))
109 {
110 *pcbSize = 2;
111 *pu64Data = (uint16_t)pParam->parval;
112 return true;
113 }
114
115 if (pParam->flags & USE_IMMEDIATE8)
116 {
117 *pcbSize = 1;
118 *pu64Data = (uint8_t)pParam->parval;
119 return true;
120 }
121
122 if (pParam->flags & USE_REG_SEG)
123 {
124 *pcbSize = 2;
125 DISFetchRegSeg(pRegFrame, pParam->base.reg_seg, (RTSEL *)pu64Data);
126 return true;
127 } /* Else - error. */
128
129 AssertFailed();
130 *pcbSize = 0;
131 *pu64Data = 0;
132 return false;
133 }
134}
135
136
137/**
138 * Saves data to 8/16/32 general purpose or segment register defined by
139 * instruction's parameter.
140 *
141 * @returns true on success.
142 * @param pCpu Pointer to current disassembler context.
143 * @param pParam Pointer to parameter of instruction to proccess.
144 * @param pRegFrame Pointer to CPUMCTXCORE guest structure.
145 * @param u64Data 8/16/32/64 bit data to store.
146 */
147bool iomSaveDataToReg(PDISCPUSTATE pCpu, PCOP_PARAMETER pParam, PCPUMCTXCORE pRegFrame, uint64_t u64Data)
148{
149 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))
150 {
151 return false;
152 }
153
154 if (pParam->flags & USE_REG_GEN32)
155 {
156 DISWriteReg32(pRegFrame, pParam->base.reg_gen, (uint32_t)u64Data);
157 return true;
158 }
159
160 if (pParam->flags & USE_REG_GEN64)
161 {
162 DISWriteReg64(pRegFrame, pParam->base.reg_gen, u64Data);
163 return true;
164 }
165
166 if (pParam->flags & USE_REG_GEN16)
167 {
168 DISWriteReg16(pRegFrame, pParam->base.reg_gen, (uint16_t)u64Data);
169 return true;
170 }
171
172 if (pParam->flags & USE_REG_GEN8)
173 {
174 DISWriteReg8(pRegFrame, pParam->base.reg_gen, (uint8_t)u64Data);
175 return true;
176 }
177
178 if (pParam->flags & USE_REG_SEG)
179 {
180 DISWriteRegSeg(pRegFrame, pParam->base.reg_seg, (RTSEL)u64Data);
181 return true;
182 }
183
184 /* Else - error. */
185 return false;
186}
187
188
189//#undef LOG_GROUP
190//#define LOG_GROUP LOG_GROUP_IOM_IOPORT
191
192/**
193 * Reads an I/O port register.
194 *
195 * @returns Strict VBox status code. Informational status codes other than the one documented
196 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
197 * @retval VINF_SUCCESS Success.
198 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
199 * status code must be passed on to EM.
200 * @retval VINF_IOM_HC_IOPORT_READ Defer the read to ring-3. (R0/GC only)
201 *
202 * @param pVM VM handle.
203 * @param Port The port to read.
204 * @param pu32Value Where to store the value read.
205 * @param cbValue The size of the register to read in bytes. 1, 2 or 4 bytes.
206 */
207VMMDECL(int) IOMIOPortRead(PVM pVM, RTIOPORT Port, uint32_t *pu32Value, size_t cbValue)
208{
209#ifdef VBOX_WITH_STATISTICS
210 /*
211 * Get the statistics record.
212 */
213 PIOMIOPORTSTATS pStats = pVM->iom.s.CTX_SUFF(pStatsLastRead);
214 if (!pStats || pStats->Core.Key != Port)
215 {
216 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTX_SUFF(pTrees)->IOPortStatTree, Port);
217 if (pStats)
218 pVM->iom.s.CTX_SUFF(pStatsLastRead) = pStats;
219 }
220#endif
221
222 /*
223 * Get handler for current context.
224 */
225 CTX_SUFF(PIOMIOPORTRANGE) pRange = pVM->iom.s.CTX_SUFF(pRangeLastRead);
226 if ( !pRange
227 || (unsigned)Port - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
228 {
229 pRange = iomIOPortGetRange(&pVM->iom.s, Port);
230 if (pRange)
231 pVM->iom.s.CTX_SUFF(pRangeLastRead) = pRange;
232 }
233 MMHYPER_RC_ASSERT_RCPTR(pVM, pRange);
234 if (pRange)
235 {
236 /*
237 * Found a range.
238 */
239#ifndef IN_RING3
240 if (!pRange->pfnInCallback)
241 {
242# ifdef VBOX_WITH_STATISTICS
243 if (pStats)
244 STAM_COUNTER_INC(&pStats->CTX_MID_Z(In,ToR3));
245# endif
246 return VINF_IOM_HC_IOPORT_READ;
247 }
248#endif
249 /* call the device. */
250#ifdef VBOX_WITH_STATISTICS
251 if (pStats)
252 STAM_PROFILE_ADV_START(&pStats->CTX_SUFF_Z(ProfIn), a);
253#endif
254 int rc = pRange->pfnInCallback(pRange->pDevIns, pRange->pvUser, Port, pu32Value, cbValue);
255#ifdef VBOX_WITH_STATISTICS
256 if (pStats)
257 STAM_PROFILE_ADV_STOP(&pStats->CTX_SUFF_Z(ProfIn), a);
258 if (rc == VINF_SUCCESS && pStats)
259 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(In));
260# ifndef IN_RING3
261 else if (rc == VINF_IOM_HC_IOPORT_READ && pStats)
262 STAM_COUNTER_INC(&pStats->CTX_MID_Z(In,ToR3));
263# endif
264#endif
265 if (rc == VERR_IOM_IOPORT_UNUSED)
266 {
267 /* make return value */
268 rc = VINF_SUCCESS;
269 switch (cbValue)
270 {
271 case 1: *(uint8_t *)pu32Value = 0xff; break;
272 case 2: *(uint16_t *)pu32Value = 0xffff; break;
273 case 4: *(uint32_t *)pu32Value = UINT32_C(0xffffffff); break;
274 default:
275 AssertMsgFailed(("Invalid I/O port size %d. Port=%d\n", cbValue, Port));
276 return VERR_IOM_INVALID_IOPORT_SIZE;
277 }
278 }
279 Log3(("IOMIOPortRead: Port=%RTiop *pu32=%08RX32 cb=%d rc=%Vrc\n", Port, *pu32Value, cbValue, rc));
280 return rc;
281 }
282
283#ifndef IN_RING3
284 /*
285 * Handler in ring-3?
286 */
287 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeR3(&pVM->iom.s, Port);
288 if (pRangeR3)
289 {
290# ifdef VBOX_WITH_STATISTICS
291 if (pStats)
292 STAM_COUNTER_INC(&pStats->CTX_MID_Z(In,ToR3));
293# endif
294 return VINF_IOM_HC_IOPORT_READ;
295 }
296#endif
297
298 /*
299 * Ok, no handler for this port.
300 */
301#ifdef VBOX_WITH_STATISTICS
302 if (pStats)
303 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(In));
304 else
305 {
306# ifndef IN_RING3
307 /* Ring-3 will have to create the statistics record. */
308 return VINF_IOM_HC_IOPORT_READ;
309# else
310 pStats = iomR3IOPortStatsCreate(pVM, Port, NULL);
311 if (pStats)
312 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(In));
313# endif
314 }
315#endif
316
317 /* make return value */
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 Log3(("IOMIOPortRead: Port=%RTiop *pu32=%08RX32 cb=%d rc=VINF_SUCCESS\n", Port, *pu32Value, cbValue));
328 return VINF_SUCCESS;
329}
330
331
332/**
333 * Reads the string buffer of an I/O port register.
334 *
335 * @returns Strict VBox status code. Informational status codes other than the one documented
336 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
337 * @retval VINF_SUCCESS Success.
338 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
339 * status code must be passed on to EM.
340 * @retval VINF_IOM_HC_IOPORT_READ Defer the read to ring-3. (R0/GC only)
341 *
342 * @param pVM VM handle.
343 * @param Port The port to read.
344 * @param pGCPtrDst Pointer to the destination buffer (GC, incremented appropriately).
345 * @param pcTransfers Pointer to the number of transfer units to read, on return remaining transfer units.
346 * @param cb Size of the transfer unit (1, 2 or 4 bytes).
347 * */
348VMMDECL(int) IOMIOPortReadString(PVM pVM, RTIOPORT Port, PRTGCPTR pGCPtrDst, PRTGCUINTREG pcTransfers, unsigned cb)
349{
350#ifdef LOG_ENABLED
351 const RTGCUINTREG cTransfers = *pcTransfers;
352#endif
353#ifdef VBOX_WITH_STATISTICS
354 /*
355 * Get the statistics record.
356 */
357 PIOMIOPORTSTATS pStats = pVM->iom.s.CTX_SUFF(pStatsLastRead);
358 if (!pStats || pStats->Core.Key != Port)
359 {
360 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTX_SUFF(pTrees)->IOPortStatTree, Port);
361 if (pStats)
362 pVM->iom.s.CTX_SUFF(pStatsLastRead) = pStats;
363 }
364#endif
365
366 /*
367 * Get handler for current context.
368 */
369 CTX_SUFF(PIOMIOPORTRANGE) pRange = pVM->iom.s.CTX_SUFF(pRangeLastRead);
370 if ( !pRange
371 || (unsigned)Port - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
372 {
373 pRange = iomIOPortGetRange(&pVM->iom.s, Port);
374 if (pRange)
375 pVM->iom.s.CTX_SUFF(pRangeLastRead) = pRange;
376 }
377 MMHYPER_RC_ASSERT_RCPTR(pVM, pRange);
378 if (pRange)
379 {
380 /*
381 * Found a range.
382 */
383#ifndef IN_RING3
384 if (!pRange->pfnInStrCallback)
385 {
386# ifdef VBOX_WITH_STATISTICS
387 if (pStats)
388 STAM_COUNTER_INC(&pStats->CTX_MID_Z(In,ToR3));
389# endif
390 return VINF_IOM_HC_IOPORT_READ;
391 }
392#endif
393 /* call the device. */
394#ifdef VBOX_WITH_STATISTICS
395 if (pStats)
396 STAM_PROFILE_ADV_START(&pStats->CTX_SUFF_Z(ProfIn), a);
397#endif
398
399 int rc = pRange->pfnInStrCallback(pRange->pDevIns, pRange->pvUser, Port, pGCPtrDst, pcTransfers, cb);
400#ifdef VBOX_WITH_STATISTICS
401 if (pStats)
402 STAM_PROFILE_ADV_STOP(&pStats->CTX_SUFF_Z(ProfIn), a);
403 if (rc == VINF_SUCCESS && pStats)
404 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(In));
405# ifndef IN_RING3
406 else if (rc == VINF_IOM_HC_IOPORT_READ && pStats)
407 STAM_COUNTER_INC(&pStats->CTX_MID_Z(In, ToR3));
408# endif
409#endif
410 Log3(("IOMIOPortReadStr: Port=%RTiop pGCPtrDst=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=%Vrc\n",
411 Port, pGCPtrDst, pcTransfers, cTransfers, *pcTransfers, cb, rc));
412 return rc;
413 }
414
415#ifndef IN_RING3
416 /*
417 * Handler in ring-3?
418 */
419 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeR3(&pVM->iom.s, Port);
420 if (pRangeR3)
421 {
422# ifdef VBOX_WITH_STATISTICS
423 if (pStats)
424 STAM_COUNTER_INC(&pStats->CTX_MID_Z(In,ToR3));
425# endif
426 return VINF_IOM_HC_IOPORT_READ;
427 }
428#endif
429
430 /*
431 * Ok, no handler for this port.
432 */
433#ifdef VBOX_WITH_STATISTICS
434 if (pStats)
435 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(In));
436 else
437 {
438# ifndef IN_RING3
439 /* Ring-3 will have to create the statistics record. */
440 return VINF_IOM_HC_IOPORT_READ;
441# else
442 pStats = iomR3IOPortStatsCreate(pVM, Port, NULL);
443 if (pStats)
444 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(In));
445# endif
446 }
447#endif
448
449 Log3(("IOMIOPortReadStr: Port=%RTiop pGCPtrDst=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=VINF_SUCCESS\n",
450 Port, pGCPtrDst, pcTransfers, cTransfers, *pcTransfers, cb));
451 return VINF_SUCCESS;
452}
453
454
455/**
456 * Writes to an I/O port register.
457 *
458 * @returns Strict VBox status code. Informational status codes other than the one documented
459 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
460 * @retval VINF_SUCCESS Success.
461 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
462 * status code must be passed on to EM.
463 * @retval VINF_IOM_HC_IOPORT_WRITE Defer the write to ring-3. (R0/GC only)
464 *
465 * @param pVM VM handle.
466 * @param Port The port to write to.
467 * @param u32Value The value to write.
468 * @param cbValue The size of the register to read in bytes. 1, 2 or 4 bytes.
469 */
470VMMDECL(int) IOMIOPortWrite(PVM pVM, RTIOPORT Port, uint32_t u32Value, size_t cbValue)
471{
472/** @todo bird: When I get time, I'll remove the GC tree and link the GC entries to the ring-3 node. */
473#ifdef VBOX_WITH_STATISTICS
474 /*
475 * Find the statistics record.
476 */
477 PIOMIOPORTSTATS pStats = pVM->iom.s.CTX_SUFF(pStatsLastWrite);
478 if (!pStats || pStats->Core.Key != Port)
479 {
480 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTX_SUFF(pTrees)->IOPortStatTree, Port);
481 if (pStats)
482 pVM->iom.s.CTX_SUFF(pStatsLastWrite) = pStats;
483 }
484#endif
485
486 /*
487 * Get handler for current context.
488 */
489 CTX_SUFF(PIOMIOPORTRANGE) pRange = pVM->iom.s.CTX_SUFF(pRangeLastWrite);
490 if ( !pRange
491 || (unsigned)Port - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
492 {
493 pRange = iomIOPortGetRange(&pVM->iom.s, Port);
494 if (pRange)
495 pVM->iom.s.CTX_SUFF(pRangeLastWrite) = pRange;
496 }
497 MMHYPER_RC_ASSERT_RCPTR(pVM, pRange);
498 if (pRange)
499 {
500 /*
501 * Found a range.
502 */
503#ifndef IN_RING3
504 if (!pRange->pfnOutCallback)
505 {
506# ifdef VBOX_WITH_STATISTICS
507 if (pStats)
508 STAM_COUNTER_INC(&pStats->CTX_MID_Z(Out,ToR3));
509# endif
510 return VINF_IOM_HC_IOPORT_WRITE;
511 }
512#endif
513 /* call the device. */
514#ifdef VBOX_WITH_STATISTICS
515 if (pStats)
516 STAM_PROFILE_ADV_START(&pStats->CTX_SUFF_Z(ProfOut), a);
517#endif
518 int rc = pRange->pfnOutCallback(pRange->pDevIns, pRange->pvUser, Port, u32Value, cbValue);
519
520#ifdef VBOX_WITH_STATISTICS
521 if (pStats)
522 STAM_PROFILE_ADV_STOP(&pStats->CTX_SUFF_Z(ProfOut), a);
523 if (rc == VINF_SUCCESS && pStats)
524 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Out));
525# ifndef IN_RING3
526 else if (rc == VINF_IOM_HC_IOPORT_WRITE && pStats)
527 STAM_COUNTER_INC(&pStats->CTX_MID_Z(Out, ToR3));
528# endif
529#endif
530 Log3(("IOMIOPortWrite: Port=%RTiop u32=%08RX32 cb=%d rc=%Vrc\n", Port, u32Value, cbValue, rc));
531 return rc;
532 }
533
534#ifndef IN_RING3
535 /*
536 * Handler in ring-3?
537 */
538 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeR3(&pVM->iom.s, Port);
539 if (pRangeR3)
540 {
541# ifdef VBOX_WITH_STATISTICS
542 if (pStats)
543 STAM_COUNTER_INC(&pStats->CTX_MID_Z(Out,ToR3));
544# endif
545 return VINF_IOM_HC_IOPORT_WRITE;
546 }
547#endif
548
549 /*
550 * Ok, no handler for that port.
551 */
552#ifdef VBOX_WITH_STATISTICS
553 /* statistics. */
554 if (pStats)
555 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Out));
556 else
557 {
558# ifndef IN_RING3
559 /* R3 will have to create the statistics record. */
560 return VINF_IOM_HC_IOPORT_WRITE;
561# else
562 pStats = iomR3IOPortStatsCreate(pVM, Port, NULL);
563 if (pStats)
564 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Out));
565# endif
566 }
567#endif
568 Log3(("IOMIOPortWrite: Port=%RTiop u32=%08RX32 cb=%d nop\n", Port, u32Value, cbValue));
569 return VINF_SUCCESS;
570}
571
572
573/**
574 * Writes the string buffer of an I/O port register.
575 *
576 * @returns Strict VBox status code. Informational status codes other than the one documented
577 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
578 * @retval VINF_SUCCESS Success.
579 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
580 * status code must be passed on to EM.
581 * @retval VINF_IOM_HC_IOPORT_WRITE Defer the write to ring-3. (R0/GC only)
582 *
583 * @param pVM VM handle.
584 * @param Port The port to write.
585 * @param pGCPtrSrc Pointer to the source buffer (GC, incremented appropriately).
586 * @param pcTransfers Pointer to the number of transfer units to write, on return remaining transfer units.
587 * @param cb Size of the transfer unit (1, 2 or 4 bytes).
588 * */
589VMMDECL(int) IOMIOPortWriteString(PVM pVM, RTIOPORT Port, PRTGCPTR pGCPtrSrc, PRTGCUINTREG pcTransfers, unsigned cb)
590{
591#ifdef LOG_ENABLED
592 const RTGCUINTREG cTransfers = *pcTransfers;
593#endif
594#ifdef VBOX_WITH_STATISTICS
595 /*
596 * Get the statistics record.
597 */
598 PIOMIOPORTSTATS pStats = pVM->iom.s.CTX_SUFF(pStatsLastWrite);
599 if (!pStats || pStats->Core.Key != Port)
600 {
601 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTX_SUFF(pTrees)->IOPortStatTree, Port);
602 if (pStats)
603 pVM->iom.s.CTX_SUFF(pStatsLastWrite) = pStats;
604 }
605#endif
606
607 /*
608 * Get handler for current context.
609 */
610 CTX_SUFF(PIOMIOPORTRANGE) pRange = pVM->iom.s.CTX_SUFF(pRangeLastWrite);
611 if ( !pRange
612 || (unsigned)Port - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
613 {
614 pRange = iomIOPortGetRange(&pVM->iom.s, Port);
615 if (pRange)
616 pVM->iom.s.CTX_SUFF(pRangeLastWrite) = pRange;
617 }
618 MMHYPER_RC_ASSERT_RCPTR(pVM, pRange);
619 if (pRange)
620 {
621 /*
622 * Found a range.
623 */
624#ifndef IN_RING3
625 if (!pRange->pfnOutStrCallback)
626 {
627# ifdef VBOX_WITH_STATISTICS
628 if (pStats)
629 STAM_COUNTER_INC(&pStats->CTX_MID_Z(Out,ToR3));
630# endif
631 return VINF_IOM_HC_IOPORT_WRITE;
632 }
633#endif
634 /* call the device. */
635#ifdef VBOX_WITH_STATISTICS
636 if (pStats)
637 STAM_PROFILE_ADV_START(&pStats->CTX_SUFF_Z(ProfOut), a);
638#endif
639 int rc = pRange->pfnOutStrCallback(pRange->pDevIns, pRange->pvUser, Port, pGCPtrSrc, pcTransfers, cb);
640#ifdef VBOX_WITH_STATISTICS
641 if (pStats)
642 STAM_PROFILE_ADV_STOP(&pStats->CTX_SUFF_Z(ProfOut), a);
643 if (rc == VINF_SUCCESS && pStats)
644 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Out));
645# ifndef IN_RING3
646 else if (rc == VINF_IOM_HC_IOPORT_WRITE && pStats)
647 STAM_COUNTER_INC(&pStats->CTX_MID_Z(Out, ToR3));
648# endif
649#endif
650 Log3(("IOMIOPortWriteStr: Port=%RTiop pGCPtrSrc=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=%Vrc\n",
651 Port, pGCPtrSrc, pcTransfers, cTransfers, *pcTransfers, cb, rc));
652 return rc;
653 }
654
655#ifndef IN_RING3
656 /*
657 * Handler in ring-3?
658 */
659 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeR3(&pVM->iom.s, Port);
660 if (pRangeR3)
661 {
662# ifdef VBOX_WITH_STATISTICS
663 if (pStats)
664 STAM_COUNTER_INC(&pStats->CTX_MID_Z(Out,ToR3));
665# endif
666 return VINF_IOM_HC_IOPORT_WRITE;
667 }
668#endif
669
670 /*
671 * Ok, no handler for this port.
672 */
673#ifdef VBOX_WITH_STATISTICS
674 if (pStats)
675 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Out));
676 else
677 {
678# ifndef IN_RING3
679 /* Ring-3 will have to create the statistics record. */
680 return VINF_IOM_HC_IOPORT_WRITE;
681# else
682 pStats = iomR3IOPortStatsCreate(pVM, Port, NULL);
683 if (pStats)
684 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Out));
685# endif
686 }
687#endif
688
689 Log3(("IOMIOPortWriteStr: Port=%RTiop pGCPtrSrc=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=VINF_SUCCESS\n",
690 Port, pGCPtrSrc, pcTransfers, cTransfers, *pcTransfers, cb));
691 return VINF_SUCCESS;
692}
693
694
695/**
696 * Checks that the operation is allowed according to the IOPL
697 * level and I/O bitmap.
698 *
699 * @returns Strict VBox status code. Informational status codes other than the one documented
700 * here are to be treated as internal failure.
701 * @retval VINF_SUCCESS Success.
702 * @retval VINF_EM_RAW_GUEST_TRAP The exception was left pending. (TRPMRaiseXcptErr)
703 * @retval VINF_TRPM_XCPT_DISPATCHED The exception was raised and dispatched for raw-mode execution. (TRPMRaiseXcptErr)
704 * @retval VINF_EM_RESCHEDULE_REM The exception was dispatched and cannot be executed in raw-mode. (TRPMRaiseXcptErr)
705 *
706 * @param pVM VM handle.
707 * @param pCtxCore Pointer to register frame.
708 * @param Port The I/O port number.
709 * @param cb The access size.
710 */
711VMMDECL(int) IOMInterpretCheckPortIOAccess(PVM pVM, PCPUMCTXCORE pCtxCore, RTIOPORT Port, unsigned cb)
712{
713 /*
714 * If this isn't ring-0, we have to check for I/O privileges.
715 */
716 uint32_t efl = CPUMRawGetEFlags(pVM, pCtxCore);
717 uint32_t cpl = CPUMGetGuestCPL(pVM, pCtxCore);
718
719 if ( ( cpl > 0
720 && X86_EFL_GET_IOPL(efl) < cpl)
721 || pCtxCore->eflags.Bits.u1VM /* IOPL is ignored in V86 mode; always check TSS bitmap */
722 )
723 {
724 /*
725 * Get TSS location and check if there can be a I/O bitmap.
726 */
727 RTGCUINTPTR GCPtrTss;
728 RTGCUINTPTR cbTss;
729 bool fCanHaveIOBitmap;
730 int rc = SELMGetTSSInfo(pVM, &GCPtrTss, &cbTss, &fCanHaveIOBitmap);
731 if (RT_FAILURE(rc))
732 {
733 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d %Vrc -> #GP(0)\n", Port, cb, rc));
734 return TRPMRaiseXcptErr(pVM, pCtxCore, X86_XCPT_GP, 0);
735 }
736
737 if ( !fCanHaveIOBitmap
738 || cbTss <= sizeof(VBOXTSS))
739 {
740 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d cbTss=%#x fCanHaveIOBitmap=%RTbool -> #GP(0)\n",
741 Port, cb, cbTss, fCanHaveIOBitmap));
742 return TRPMRaiseXcptErr(pVM, pCtxCore, X86_XCPT_GP, 0);
743 }
744
745 /*
746 * Fetch the I/O bitmap offset.
747 */
748 uint16_t offIOPB;
749 rc = PGMPhysInterpretedRead(pVM, pCtxCore, &offIOPB, GCPtrTss + RT_OFFSETOF(VBOXTSS, offIoBitmap), sizeof(offIOPB));
750 if (rc != VINF_SUCCESS)
751 {
752 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d GCPtrTss=%VGv %Vrc\n",
753 Port, cb, GCPtrTss, rc));
754 return rc;
755 }
756
757 /*
758 * Check the limit and read the two bitmap bytes.
759 */
760 uint32_t offTss = offIOPB + (Port >> 3);
761 if (offTss + 1 >= cbTss)
762 {
763 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d offTss=%#x cbTss=%#x -> #GP(0)\n",
764 Port, cb, offTss, cbTss));
765 return TRPMRaiseXcptErr(pVM, pCtxCore, X86_XCPT_GP, 0);
766 }
767 uint16_t u16;
768 rc = PGMPhysInterpretedRead(pVM, pCtxCore, &u16, GCPtrTss + offTss, sizeof(u16));
769 if (rc != VINF_SUCCESS)
770 {
771 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d GCPtrTss=%VGv offTss=%#x -> %Vrc\n",
772 Port, cb, GCPtrTss, offTss, rc));
773 return rc;
774 }
775
776 /*
777 * All the bits must be clear.
778 */
779 if ((u16 >> (Port & 7)) & ((1 << cb) - 1))
780 {
781 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d u16=%#x -> #GP(0)\n",
782 Port, cb, u16, offTss));
783 return TRPMRaiseXcptErr(pVM, pCtxCore, X86_XCPT_GP, 0);
784 }
785 LogFlow(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d offTss=%#x cbTss=%#x u16=%#x -> OK\n",
786 Port, cb, u16, offTss, cbTss));
787 }
788 return VINF_SUCCESS;
789}
790
791
792/**
793 * IN <AL|AX|EAX>, <DX|imm16>
794 *
795 * @returns Strict VBox status code. Informational status codes other than the one documented
796 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
797 * @retval VINF_SUCCESS Success.
798 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
799 * status code must be passed on to EM.
800 * @retval VINF_IOM_HC_IOPORT_READ Defer the read to ring-3. (R0/GC only)
801 * @retval VINF_EM_RAW_GUEST_TRAP The exception was left pending. (TRPMRaiseXcptErr)
802 * @retval VINF_TRPM_XCPT_DISPATCHED The exception was raised and dispatched for raw-mode execution. (TRPMRaiseXcptErr)
803 * @retval VINF_EM_RESCHEDULE_REM The exception was dispatched and cannot be executed in raw-mode. (TRPMRaiseXcptErr)
804 *
805 * @param pVM The virtual machine (GC pointer ofcourse).
806 * @param pRegFrame Pointer to CPUMCTXCORE guest registers structure.
807 * @param pCpu Disassembler CPU state.
808 */
809VMMDECL(int) IOMInterpretIN(PVM pVM, PCPUMCTXCORE pRegFrame, PDISCPUSTATE pCpu)
810{
811#ifdef IN_GC
812 STAM_COUNTER_INC(&pVM->iom.s.StatInstIn);
813#endif
814
815 /*
816 * Get port number from second parameter.
817 * And get the register size from the first parameter.
818 */
819 uint64_t uPort = 0;
820 unsigned cbSize = 0;
821 bool fRc = iomGetRegImmData(pCpu, &pCpu->param2, pRegFrame, &uPort, &cbSize);
822 AssertMsg(fRc, ("Failed to get reg/imm port number!\n")); NOREF(fRc);
823
824 cbSize = DISGetParamSize(pCpu, &pCpu->param1);
825 Assert(cbSize > 0);
826 int rc = IOMInterpretCheckPortIOAccess(pVM, pRegFrame, uPort, cbSize);
827 if (rc == VINF_SUCCESS)
828 {
829 /*
830 * Attemp to read the port.
831 */
832 uint32_t u32Data = UINT32_C(0xffffffff);
833 rc = IOMIOPortRead(pVM, uPort, &u32Data, cbSize);
834 if (IOM_SUCCESS(rc))
835 {
836 /*
837 * Store the result in the AL|AX|EAX register.
838 */
839 fRc = iomSaveDataToReg(pCpu, &pCpu->param1, pRegFrame, u32Data);
840 AssertMsg(fRc, ("Failed to store register value!\n")); NOREF(fRc);
841 }
842 else
843 AssertMsg(rc == VINF_IOM_HC_IOPORT_READ || RT_FAILURE(rc), ("%Vrc\n", rc));
844 }
845 else
846 AssertMsg(rc == VINF_EM_RAW_GUEST_TRAP || rc == VINF_TRPM_XCPT_DISPATCHED || rc == VINF_TRPM_XCPT_DISPATCHED || RT_FAILURE(rc), ("%Vrc\n", rc));
847 return rc;
848}
849
850
851/**
852 * OUT <DX|imm16>, <AL|AX|EAX>
853 *
854 * @returns Strict VBox status code. Informational status codes other than the one documented
855 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
856 * @retval VINF_SUCCESS Success.
857 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
858 * status code must be passed on to EM.
859 * @retval VINF_IOM_HC_IOPORT_WRITE Defer the write to ring-3. (R0/GC only)
860 * @retval VINF_EM_RAW_GUEST_TRAP The exception was left pending. (TRPMRaiseXcptErr)
861 * @retval VINF_TRPM_XCPT_DISPATCHED The exception was raised and dispatched for raw-mode execution. (TRPMRaiseXcptErr)
862 * @retval VINF_EM_RESCHEDULE_REM The exception was dispatched and cannot be executed in raw-mode. (TRPMRaiseXcptErr)
863 *
864 * @param pVM The virtual machine (GC pointer ofcourse).
865 * @param pRegFrame Pointer to CPUMCTXCORE guest registers structure.
866 * @param pCpu Disassembler CPU state.
867 */
868VMMDECL(int) IOMInterpretOUT(PVM pVM, PCPUMCTXCORE pRegFrame, PDISCPUSTATE pCpu)
869{
870#ifdef IN_GC
871 STAM_COUNTER_INC(&pVM->iom.s.StatInstOut);
872#endif
873
874 /*
875 * Get port number from first parameter.
876 * And get the register size and value from the second parameter.
877 */
878 uint64_t uPort = 0;
879 unsigned cbSize = 0;
880 bool fRc = iomGetRegImmData(pCpu, &pCpu->param1, pRegFrame, &uPort, &cbSize);
881 AssertMsg(fRc, ("Failed to get reg/imm port number!\n")); NOREF(fRc);
882
883 int rc = IOMInterpretCheckPortIOAccess(pVM, pRegFrame, uPort, cbSize);
884 if (rc == VINF_SUCCESS)
885 {
886 uint64_t u64Data = 0;
887 fRc = iomGetRegImmData(pCpu, &pCpu->param2, pRegFrame, &u64Data, &cbSize);
888 AssertMsg(fRc, ("Failed to get reg value!\n")); NOREF(fRc);
889
890 /*
891 * Attempt to write to the port.
892 */
893 rc = IOMIOPortWrite(pVM, uPort, u64Data, cbSize);
894 AssertMsg(rc == VINF_SUCCESS || rc == VINF_IOM_HC_IOPORT_WRITE || (rc >= VINF_EM_FIRST && rc <= VINF_EM_LAST) || RT_FAILURE(rc), ("%Vrc\n", rc));
895 }
896 else
897 AssertMsg(rc == VINF_EM_RAW_GUEST_TRAP || rc == VINF_TRPM_XCPT_DISPATCHED || rc == VINF_TRPM_XCPT_DISPATCHED || RT_FAILURE(rc), ("%Vrc\n", rc));
898 return rc;
899}
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