VirtualBox

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

Last change on this file since 12559 was 11456, checked in by vboxsync, 16 years ago

Use DISGetParamSize instead of iomGetRegSize.

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