VirtualBox

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

Last change on this file since 8170 was 8155, checked in by vboxsync, 17 years ago

The Big Sun Rebranding Header Change

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