VirtualBox

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

Last change on this file since 10171 was 9774, checked in by vboxsync, 17 years ago

IOM updates for 64 bits mode.

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