VirtualBox

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

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

Protect port I/O with a critical section.

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