VirtualBox

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

Last change on this file since 22493 was 22493, checked in by vboxsync, 15 years ago

VMM,DevPCI,VBox/types.h: Added a VBOXSTRICTRC type for indicating strict VBox stuatus codes. Some expirmentation with making it a class in strict builds to get some help from the compiler with making sure the return code is treated correctly.

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