VirtualBox

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

Last change on this file since 8863 was 8354, checked in by vboxsync, 17 years ago

Accidental commit

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