VirtualBox

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

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

Some more locking updates

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 32.6 KB
Line 
1/* $Id: IOMAll.cpp 19807 2009-05-19 09:01:05Z 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(int) 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 rc = iomLock(pVM);
265#ifndef IN_RING3
266 if (rc == VERR_SEM_BUSY)
267 return VINF_IOM_HC_IOPORT_READ;
268#else
269 AssertRC(rc);
270#endif
271
272#ifdef VBOX_WITH_STATISTICS
273 /*
274 * Get the statistics record.
275 */
276 PIOMIOPORTSTATS pStats = pVM->iom.s.CTX_SUFF(pStatsLastRead);
277 if (!pStats || pStats->Core.Key != Port)
278 {
279 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTX_SUFF(pTrees)->IOPortStatTree, Port);
280 if (pStats)
281 pVM->iom.s.CTX_SUFF(pStatsLastRead) = pStats;
282 }
283#endif
284
285 /*
286 * Get handler for current context.
287 */
288 CTX_SUFF(PIOMIOPORTRANGE) pRange = pVM->iom.s.CTX_SUFF(pRangeLastRead);
289 if ( !pRange
290 || (unsigned)Port - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
291 {
292 pRange = iomIOPortGetRange(&pVM->iom.s, Port);
293 if (pRange)
294 pVM->iom.s.CTX_SUFF(pRangeLastRead) = pRange;
295 }
296 MMHYPER_RC_ASSERT_RCPTR(pVM, pRange);
297 if (pRange)
298 {
299 /*
300 * Found a range.
301 */
302#ifndef IN_RING3
303 if (!pRange->pfnInCallback)
304 {
305# ifdef VBOX_WITH_STATISTICS
306 if (pStats)
307 STAM_COUNTER_INC(&pStats->CTX_MID_Z(In,ToR3));
308# endif
309 iomUnlock(pVM);
310 return VINF_IOM_HC_IOPORT_READ;
311 }
312#endif
313 /* call the device. */
314#ifdef VBOX_WITH_STATISTICS
315 if (pStats)
316 STAM_PROFILE_ADV_START(&pStats->CTX_SUFF_Z(ProfIn), a);
317#endif
318 rc = pRange->pfnInCallback(pRange->pDevIns, pRange->pvUser, Port, pu32Value, (unsigned)cbValue);
319#ifdef VBOX_WITH_STATISTICS
320 if (pStats)
321 STAM_PROFILE_ADV_STOP(&pStats->CTX_SUFF_Z(ProfIn), a);
322 if (rc == VINF_SUCCESS && pStats)
323 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(In));
324# ifndef IN_RING3
325 else if (rc == VINF_IOM_HC_IOPORT_READ && pStats)
326 STAM_COUNTER_INC(&pStats->CTX_MID_Z(In,ToR3));
327# endif
328#endif
329 if (rc == VERR_IOM_IOPORT_UNUSED)
330 {
331 /* make return value */
332 rc = VINF_SUCCESS;
333 switch (cbValue)
334 {
335 case 1: *(uint8_t *)pu32Value = 0xff; break;
336 case 2: *(uint16_t *)pu32Value = 0xffff; break;
337 case 4: *(uint32_t *)pu32Value = UINT32_C(0xffffffff); break;
338 default:
339 AssertMsgFailed(("Invalid I/O port size %d. Port=%d\n", cbValue, Port));
340 iomUnlock(pVM);
341 return VERR_IOM_INVALID_IOPORT_SIZE;
342 }
343 }
344 Log3(("IOMIOPortRead: Port=%RTiop *pu32=%08RX32 cb=%d rc=%Rrc\n", Port, *pu32Value, cbValue, rc));
345 iomUnlock(pVM);
346 return rc;
347 }
348
349#ifndef IN_RING3
350 /*
351 * Handler in ring-3?
352 */
353 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeR3(&pVM->iom.s, Port);
354 if (pRangeR3)
355 {
356# ifdef VBOX_WITH_STATISTICS
357 if (pStats)
358 STAM_COUNTER_INC(&pStats->CTX_MID_Z(In,ToR3));
359# endif
360 iomUnlock(pVM);
361 return VINF_IOM_HC_IOPORT_READ;
362 }
363#endif
364
365 /*
366 * Ok, no handler for this port.
367 */
368#ifdef VBOX_WITH_STATISTICS
369 if (pStats)
370 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(In));
371 else
372 {
373# ifndef IN_RING3
374 /* Ring-3 will have to create the statistics record. */
375 iomUnlock(pVM);
376 return VINF_IOM_HC_IOPORT_READ;
377# else
378 pStats = iomR3IOPortStatsCreate(pVM, Port, NULL);
379 if (pStats)
380 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(In));
381# endif
382 }
383#endif
384
385 /* make return value */
386 switch (cbValue)
387 {
388 case 1: *(uint8_t *)pu32Value = 0xff; break;
389 case 2: *(uint16_t *)pu32Value = 0xffff; break;
390 case 4: *(uint32_t *)pu32Value = UINT32_C(0xffffffff); break;
391 default:
392 AssertMsgFailed(("Invalid I/O port size %d. Port=%d\n", cbValue, Port));
393 iomUnlock(pVM);
394 return VERR_IOM_INVALID_IOPORT_SIZE;
395 }
396 Log3(("IOMIOPortRead: Port=%RTiop *pu32=%08RX32 cb=%d rc=VINF_SUCCESS\n", Port, *pu32Value, cbValue));
397 iomUnlock(pVM);
398 return VINF_SUCCESS;
399}
400
401
402/**
403 * Reads the string buffer of an I/O port register.
404 *
405 * @returns Strict VBox status code. Informational status codes other than the one documented
406 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
407 * @retval VINF_SUCCESS Success.
408 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
409 * status code must be passed on to EM.
410 * @retval VINF_IOM_HC_IOPORT_READ Defer the read to ring-3. (R0/GC only)
411 *
412 * @param pVM VM handle.
413 * @param Port The port to read.
414 * @param pGCPtrDst Pointer to the destination buffer (GC, incremented appropriately).
415 * @param pcTransfers Pointer to the number of transfer units to read, on return remaining transfer units.
416 * @param cb Size of the transfer unit (1, 2 or 4 bytes).
417 * */
418VMMDECL(int) IOMIOPortReadString(PVM pVM, RTIOPORT Port, PRTGCPTR pGCPtrDst, PRTGCUINTREG pcTransfers, unsigned cb)
419{
420 /* Take the IOM lock before performing any device I/O. */
421 int rc = iomLock(pVM);
422#ifndef IN_RING3
423 if (rc == VERR_SEM_BUSY)
424 return VINF_IOM_HC_IOPORT_READ;
425#endif
426 AssertRC(rc);
427
428#ifdef LOG_ENABLED
429 const RTGCUINTREG cTransfers = *pcTransfers;
430#endif
431#ifdef VBOX_WITH_STATISTICS
432 /*
433 * Get the statistics record.
434 */
435 PIOMIOPORTSTATS pStats = pVM->iom.s.CTX_SUFF(pStatsLastRead);
436 if (!pStats || pStats->Core.Key != Port)
437 {
438 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTX_SUFF(pTrees)->IOPortStatTree, Port);
439 if (pStats)
440 pVM->iom.s.CTX_SUFF(pStatsLastRead) = pStats;
441 }
442#endif
443
444 /*
445 * Get handler for current context.
446 */
447 CTX_SUFF(PIOMIOPORTRANGE) pRange = pVM->iom.s.CTX_SUFF(pRangeLastRead);
448 if ( !pRange
449 || (unsigned)Port - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
450 {
451 pRange = iomIOPortGetRange(&pVM->iom.s, Port);
452 if (pRange)
453 pVM->iom.s.CTX_SUFF(pRangeLastRead) = pRange;
454 }
455 MMHYPER_RC_ASSERT_RCPTR(pVM, pRange);
456 if (pRange)
457 {
458 /*
459 * Found a range.
460 */
461#ifndef IN_RING3
462 if (!pRange->pfnInStrCallback)
463 {
464# ifdef VBOX_WITH_STATISTICS
465 if (pStats)
466 STAM_COUNTER_INC(&pStats->CTX_MID_Z(In,ToR3));
467# endif
468 iomUnlock(pVM);
469 return VINF_IOM_HC_IOPORT_READ;
470 }
471#endif
472 /* call the device. */
473#ifdef VBOX_WITH_STATISTICS
474 if (pStats)
475 STAM_PROFILE_ADV_START(&pStats->CTX_SUFF_Z(ProfIn), a);
476#endif
477
478 int rc = pRange->pfnInStrCallback(pRange->pDevIns, pRange->pvUser, Port, pGCPtrDst, pcTransfers, cb);
479#ifdef VBOX_WITH_STATISTICS
480 if (pStats)
481 STAM_PROFILE_ADV_STOP(&pStats->CTX_SUFF_Z(ProfIn), a);
482 if (rc == VINF_SUCCESS && pStats)
483 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(In));
484# ifndef IN_RING3
485 else if (rc == VINF_IOM_HC_IOPORT_READ && pStats)
486 STAM_COUNTER_INC(&pStats->CTX_MID_Z(In, ToR3));
487# endif
488#endif
489 Log3(("IOMIOPortReadStr: Port=%RTiop pGCPtrDst=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=%Rrc\n",
490 Port, pGCPtrDst, pcTransfers, cTransfers, *pcTransfers, cb, rc));
491 iomUnlock(pVM);
492 return rc;
493 }
494
495#ifndef IN_RING3
496 /*
497 * Handler in ring-3?
498 */
499 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeR3(&pVM->iom.s, Port);
500 if (pRangeR3)
501 {
502# ifdef VBOX_WITH_STATISTICS
503 if (pStats)
504 STAM_COUNTER_INC(&pStats->CTX_MID_Z(In,ToR3));
505# endif
506 iomUnlock(pVM);
507 return VINF_IOM_HC_IOPORT_READ;
508 }
509#endif
510
511 /*
512 * Ok, no handler for this port.
513 */
514#ifdef VBOX_WITH_STATISTICS
515 if (pStats)
516 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(In));
517 else
518 {
519# ifndef IN_RING3
520 /* Ring-3 will have to create the statistics record. */
521 iomUnlock(pVM);
522 return VINF_IOM_HC_IOPORT_READ;
523# else
524 pStats = iomR3IOPortStatsCreate(pVM, Port, NULL);
525 if (pStats)
526 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(In));
527# endif
528 }
529#endif
530
531 Log3(("IOMIOPortReadStr: Port=%RTiop pGCPtrDst=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=VINF_SUCCESS\n",
532 Port, pGCPtrDst, pcTransfers, cTransfers, *pcTransfers, cb));
533 iomUnlock(pVM);
534 return VINF_SUCCESS;
535}
536
537
538/**
539 * Writes to an I/O port register.
540 *
541 * @returns Strict VBox status code. Informational status codes other than the one documented
542 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
543 * @retval VINF_SUCCESS Success.
544 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
545 * status code must be passed on to EM.
546 * @retval VINF_IOM_HC_IOPORT_WRITE Defer the write to ring-3. (R0/GC only)
547 *
548 * @param pVM VM handle.
549 * @param Port The port to write to.
550 * @param u32Value The value to write.
551 * @param cbValue The size of the register to read in bytes. 1, 2 or 4 bytes.
552 */
553VMMDECL(int) IOMIOPortWrite(PVM pVM, RTIOPORT Port, uint32_t u32Value, size_t cbValue)
554{
555 /* Take the IOM lock before performing any device I/O. */
556 int rc = iomLock(pVM);
557#ifndef IN_RING3
558 if (rc == VERR_SEM_BUSY)
559 return VINF_IOM_HC_IOPORT_WRITE;
560#endif
561 AssertRC(rc);
562
563/** @todo bird: When I get time, I'll remove the GC tree and link the GC 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 int rc = 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 (rc == VINF_SUCCESS && pStats)
616 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Out));
617# ifndef IN_RING3
618 else if (rc == 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, rc));
623 iomUnlock(pVM);
624 return rc;
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(int) 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 rc = iomLock(pVM);
689#ifndef IN_RING3
690 if (rc == VERR_SEM_BUSY)
691 return VINF_IOM_HC_IOPORT_WRITE;
692#endif
693 AssertRC(rc);
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 int rc = 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 (rc == VINF_SUCCESS && pStats)
749 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Out));
750# ifndef IN_RING3
751 else if (rc == 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 rc=%Rrc\n",
756 Port, pGCPtrSrc, pcTransfers, cTransfers, *pcTransfers, cb, rc));
757 iomUnlock(pVM);
758 return rc;
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(int) 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 rc = SELMGetTSSInfo(pVM, pVCpu, &GCPtrTss, &cbTss, &fCanHaveIOBitmap);
842 if (RT_FAILURE(rc))
843 {
844 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d %Rrc -> #GP(0)\n", Port, cb, rc));
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 rc = PGMPhysInterpretedRead(pVCpu, pCtxCore, &offIOPB, GCPtrTss + RT_OFFSETOF(VBOXTSS, offIoBitmap), sizeof(offIOPB));
861 if (rc != VINF_SUCCESS)
862 {
863 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d GCPtrTss=%RGv %Rrc\n",
864 Port, cb, GCPtrTss, rc));
865 return rc;
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 rc = PGMPhysInterpretedRead(pVCpu, pCtxCore, &u16, GCPtrTss + offTss, sizeof(u16));
880 if (rc != VINF_SUCCESS)
881 {
882 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d GCPtrTss=%RGv offTss=%#x -> %Rrc\n",
883 Port, cb, GCPtrTss, offTss, rc));
884 return rc;
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(int) 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 int rc = IOMInterpretCheckPortIOAccess(pVM, pRegFrame, uPort, cbSize);
938 if (rc == VINF_SUCCESS)
939 {
940 /*
941 * Attemp to read the port.
942 */
943 uint32_t u32Data = UINT32_C(0xffffffff);
944 rc = IOMIOPortRead(pVM, uPort, &u32Data, cbSize);
945 if (IOM_SUCCESS(rc))
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(rc == VINF_IOM_HC_IOPORT_READ || RT_FAILURE(rc), ("%Rrc\n", rc));
955 }
956 else
957 AssertMsg(rc == VINF_EM_RAW_GUEST_TRAP || rc == VINF_TRPM_XCPT_DISPATCHED || rc == VINF_TRPM_XCPT_DISPATCHED || RT_FAILURE(rc), ("%Rrc\n", rc));
958 return rc;
959}
960
961
962/**
963 * OUT <DX|imm16>, <AL|AX|EAX>
964 *
965 * @returns Strict VBox status code. Informational status codes other than the one documented
966 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
967 * @retval VINF_SUCCESS Success.
968 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
969 * status code must be passed on to EM.
970 * @retval VINF_IOM_HC_IOPORT_WRITE Defer the write to ring-3. (R0/GC only)
971 * @retval VINF_EM_RAW_GUEST_TRAP The exception was left pending. (TRPMRaiseXcptErr)
972 * @retval VINF_TRPM_XCPT_DISPATCHED The exception was raised and dispatched for raw-mode execution. (TRPMRaiseXcptErr)
973 * @retval VINF_EM_RESCHEDULE_REM The exception was dispatched and cannot be executed in raw-mode. (TRPMRaiseXcptErr)
974 *
975 * @param pVM The virtual machine (GC pointer ofcourse).
976 * @param pRegFrame Pointer to CPUMCTXCORE guest registers structure.
977 * @param pCpu Disassembler CPU state.
978 */
979VMMDECL(int) IOMInterpretOUT(PVM pVM, PCPUMCTXCORE pRegFrame, PDISCPUSTATE pCpu)
980{
981#ifdef IN_RC
982 STAM_COUNTER_INC(&pVM->iom.s.StatInstOut);
983#endif
984
985 /*
986 * Get port number from first parameter.
987 * And get the register size and value from the second parameter.
988 */
989 uint64_t uPort = 0;
990 unsigned cbSize = 0;
991 bool fRc = iomGetRegImmData(pCpu, &pCpu->param1, pRegFrame, &uPort, &cbSize);
992 AssertMsg(fRc, ("Failed to get reg/imm port number!\n")); NOREF(fRc);
993
994 int rc = IOMInterpretCheckPortIOAccess(pVM, pRegFrame, uPort, cbSize);
995 if (rc == VINF_SUCCESS)
996 {
997 uint64_t u64Data = 0;
998 fRc = iomGetRegImmData(pCpu, &pCpu->param2, pRegFrame, &u64Data, &cbSize);
999 AssertMsg(fRc, ("Failed to get reg value!\n")); NOREF(fRc);
1000
1001 /*
1002 * Attempt to write to the port.
1003 */
1004 rc = IOMIOPortWrite(pVM, uPort, u64Data, cbSize);
1005 AssertMsg(rc == VINF_SUCCESS || rc == VINF_IOM_HC_IOPORT_WRITE || (rc >= VINF_EM_FIRST && rc <= VINF_EM_LAST) || RT_FAILURE(rc), ("%Rrc\n", rc));
1006 }
1007 else
1008 AssertMsg(rc == VINF_EM_RAW_GUEST_TRAP || rc == VINF_TRPM_XCPT_DISPATCHED || rc == VINF_TRPM_XCPT_DISPATCHED || RT_FAILURE(rc), ("%Rrc\n", rc));
1009 return rc;
1010}
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