VirtualBox

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

Last change on this file since 84044 was 82968, checked in by vboxsync, 4 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 22.7 KB
Line 
1/* $Id: IOMAll.cpp 82968 2020-02-04 10:35:17Z vboxsync $ */
2/** @file
3 * IOM - Input / Output Monitor - Any Context.
4 */
5
6/*
7 * Copyright (C) 2006-2020 Oracle Corporation
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
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_IOM_IOPORT
23#include <VBox/vmm/iom.h>
24#include <VBox/vmm/mm.h>
25#include <VBox/param.h>
26#include "IOMInternal.h"
27#include <VBox/vmm/vmcc.h>
28#include <VBox/vmm/vmm.h>
29#include <VBox/vmm/selm.h>
30#include <VBox/vmm/trpm.h>
31#include <VBox/vmm/pdmdev.h>
32#include <VBox/vmm/pgm.h>
33#include <VBox/vmm/cpum.h>
34#include <VBox/err.h>
35#include <VBox/log.h>
36#include <iprt/assert.h>
37#include <iprt/string.h>
38#include "IOMInline.h"
39
40
41/**
42 * Reads an I/O port register.
43 *
44 * @returns Strict VBox status code. Informational status codes other than the one documented
45 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
46 * @retval VINF_SUCCESS Success.
47 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
48 * status code must be passed on to EM.
49 * @retval VINF_IOM_R3_IOPORT_READ Defer the read to ring-3. (R0/RC only)
50 *
51 * @param pVM The cross context VM structure.
52 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
53 * @param Port The port to read.
54 * @param pu32Value Where to store the value read.
55 * @param cbValue The size of the register to read in bytes. 1, 2 or 4 bytes.
56 */
57VMMDECL(VBOXSTRICTRC) IOMIOPortRead(PVMCC pVM, PVMCPU pVCpu, RTIOPORT Port, uint32_t *pu32Value, size_t cbValue)
58{
59 STAM_COUNTER_INC(&pVM->iom.s.StatIoPortIn);
60 Assert(pVCpu->iom.s.PendingIOPortWrite.cbValue == 0);
61
62/** @todo should initialize *pu32Value here because it can happen that some
63 * handle is buggy and doesn't handle all cases. */
64
65 /* For lookups we need to share lock IOM. */
66 int rc2 = IOM_LOCK_SHARED(pVM);
67#ifndef IN_RING3
68 if (rc2 == VERR_SEM_BUSY)
69 return VINF_IOM_R3_IOPORT_READ;
70#endif
71 AssertRC(rc2);
72
73 /*
74 * Get the entry for the current context.
75 */
76 uint16_t offPort;
77 CTX_SUFF(PIOMIOPORTENTRY) pRegEntry = iomIoPortGetEntry(pVM, Port, &offPort, &pVCpu->iom.s.idxIoPortLastRead);
78 if (pRegEntry)
79 {
80#ifdef VBOX_WITH_STATISTICS
81 PIOMIOPORTSTATSENTRY pStats = iomIoPortGetStats(pVM, pRegEntry, offPort);
82#endif
83
84 /*
85 * Found an entry, get the data so we can leave the IOM lock.
86 */
87 uint16_t const fFlags = pRegEntry->fFlags;
88 PFNIOMIOPORTNEWIN pfnInCallback = pRegEntry->pfnInCallback;
89 PPDMDEVINS pDevIns = pRegEntry->pDevIns;
90#ifndef IN_RING3
91 if ( pfnInCallback
92 && pDevIns
93 && pRegEntry->cPorts > 0)
94 { /* likely */ }
95 else
96 {
97 STAM_COUNTER_INC(&pStats->InRZToR3);
98 IOM_UNLOCK_SHARED(pVM);
99 return VINF_IOM_R3_IOPORT_READ;
100 }
101#endif
102 void *pvUser = pRegEntry->pvUser;
103 IOM_UNLOCK_SHARED(pVM);
104 AssertPtr(pDevIns);
105 AssertPtr(pfnInCallback);
106
107 /*
108 * Call the device.
109 */
110 VBOXSTRICTRC rcStrict = PDMCritSectEnter(pDevIns->CTX_SUFF(pCritSectRo), VINF_IOM_R3_IOPORT_READ);
111 if (rcStrict == VINF_SUCCESS)
112 {
113 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfIn), a);
114 rcStrict = pfnInCallback(pDevIns, pvUser, fFlags & IOM_IOPORT_F_ABS ? Port : offPort, pu32Value, (unsigned)cbValue);
115 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfIn), a);
116 PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo));
117
118#ifndef IN_RING3
119 if (rcStrict == VINF_IOM_R3_IOPORT_READ)
120 STAM_COUNTER_INC(&pStats->InRZToR3);
121 else
122#endif
123 {
124 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(In));
125 STAM_COUNTER_INC(&iomIoPortGetStats(pVM, pRegEntry, 0)->Total);
126 if (rcStrict == VERR_IOM_IOPORT_UNUSED)
127 {
128 /* make return value */
129 rcStrict = VINF_SUCCESS;
130 switch (cbValue)
131 {
132 case 1: *(uint8_t *)pu32Value = 0xff; break;
133 case 2: *(uint16_t *)pu32Value = 0xffff; break;
134 case 4: *(uint32_t *)pu32Value = UINT32_C(0xffffffff); break;
135 default:
136 AssertMsgFailedReturn(("Invalid I/O port size %d. Port=%d\n", cbValue, Port), VERR_IOM_INVALID_IOPORT_SIZE);
137 }
138 }
139 }
140 Log3(("IOMIOPortRead: Port=%RTiop *pu32=%08RX32 cb=%d rc=%Rrc\n", Port, *pu32Value, cbValue, VBOXSTRICTRC_VAL(rcStrict)));
141 STAM_COUNTER_INC(&iomIoPortGetStats(pVM, pRegEntry, 0)->Total);
142 }
143 else
144 STAM_COUNTER_INC(&pStats->InRZToR3);
145 return rcStrict;
146 }
147
148 /*
149 * Ok, no handler for this port.
150 */
151 IOM_UNLOCK_SHARED(pVM);
152 switch (cbValue)
153 {
154 case 1: *(uint8_t *)pu32Value = 0xff; break;
155 case 2: *(uint16_t *)pu32Value = 0xffff; break;
156 case 4: *(uint32_t *)pu32Value = UINT32_C(0xffffffff); break;
157 default:
158 AssertMsgFailedReturn(("Invalid I/O port size %d. Port=%d\n", cbValue, Port), VERR_IOM_INVALID_IOPORT_SIZE);
159 }
160 Log3(("IOMIOPortRead: Port=%RTiop *pu32=%08RX32 cb=%d rc=VINF_SUCCESS\n", Port, *pu32Value, cbValue));
161 return VINF_SUCCESS;
162}
163
164
165/**
166 * Reads the string buffer of an I/O port register.
167 *
168 * @returns Strict VBox status code. Informational status codes other than the one documented
169 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
170 * @retval VINF_SUCCESS Success or no string I/O callback in
171 * this context.
172 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
173 * status code must be passed on to EM.
174 * @retval VINF_IOM_R3_IOPORT_READ Defer the read to ring-3. (R0/RC only)
175 *
176 * @param pVM The cross context VM structure.
177 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
178 * @param uPort The port to read.
179 * @param pvDst Pointer to the destination buffer.
180 * @param pcTransfers Pointer to the number of transfer units to read, on return remaining transfer units.
181 * @param cb Size of the transfer unit (1, 2 or 4 bytes).
182 */
183VMM_INT_DECL(VBOXSTRICTRC) IOMIOPortReadString(PVMCC pVM, PVMCPU pVCpu, RTIOPORT uPort,
184 void *pvDst, uint32_t *pcTransfers, unsigned cb)
185{
186 STAM_COUNTER_INC(&pVM->iom.s.StatIoPortInS);
187 Assert(pVCpu->iom.s.PendingIOPortWrite.cbValue == 0);
188
189 /* For lookups we need to share lock IOM. */
190 int rc2 = IOM_LOCK_SHARED(pVM);
191#ifndef IN_RING3
192 if (rc2 == VERR_SEM_BUSY)
193 return VINF_IOM_R3_IOPORT_READ;
194#endif
195 AssertRC(rc2);
196
197 const uint32_t cRequestedTransfers = *pcTransfers;
198 Assert(cRequestedTransfers > 0);
199
200 /*
201 * Get the entry for the current context.
202 */
203 uint16_t offPort;
204 CTX_SUFF(PIOMIOPORTENTRY) pRegEntry = iomIoPortGetEntry(pVM, uPort, &offPort, &pVCpu->iom.s.idxIoPortLastReadStr);
205 if (pRegEntry)
206 {
207#ifdef VBOX_WITH_STATISTICS
208 PIOMIOPORTSTATSENTRY pStats = iomIoPortGetStats(pVM, pRegEntry, offPort);
209#endif
210
211 /*
212 * Found an entry, get the data so we can leave the IOM lock.
213 */
214 uint16_t const fFlags = pRegEntry->fFlags;
215 PFNIOMIOPORTNEWINSTRING pfnInStrCallback = pRegEntry->pfnInStrCallback;
216 PFNIOMIOPORTNEWIN pfnInCallback = pRegEntry->pfnInCallback;
217 PPDMDEVINS pDevIns = pRegEntry->pDevIns;
218#ifndef IN_RING3
219 if ( pfnInCallback
220 && pDevIns
221 && pRegEntry->cPorts > 0)
222 { /* likely */ }
223 else
224 {
225 STAM_COUNTER_INC(&pStats->InRZToR3);
226 IOM_UNLOCK_SHARED(pVM);
227 return VINF_IOM_R3_IOPORT_READ;
228 }
229#endif
230 void *pvUser = pRegEntry->pvUser;
231 IOM_UNLOCK_SHARED(pVM);
232 AssertPtr(pDevIns);
233 AssertPtr(pfnInCallback);
234
235 /*
236 * Call the device.
237 */
238 VBOXSTRICTRC rcStrict = PDMCritSectEnter(pDevIns->CTX_SUFF(pCritSectRo), VINF_IOM_R3_IOPORT_READ);
239 if (rcStrict == VINF_SUCCESS)
240 {
241 /*
242 * First using the string I/O callback.
243 */
244 if (pfnInStrCallback)
245 {
246 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfIn), a);
247 rcStrict = pfnInStrCallback(pDevIns, pvUser, fFlags & IOM_IOPORT_F_ABS ? uPort : offPort,
248 (uint8_t *)pvDst, pcTransfers, cb);
249 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfIn), a);
250 }
251
252 /*
253 * Then doing the single I/O fallback.
254 */
255 if ( *pcTransfers > 0
256 && rcStrict == VINF_SUCCESS)
257 {
258 pvDst = (uint8_t *)pvDst + (cRequestedTransfers - *pcTransfers) * cb;
259 do
260 {
261 uint32_t u32Value = 0;
262 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfIn), a);
263 rcStrict = pfnInCallback(pDevIns, pvUser, fFlags & IOM_IOPORT_F_ABS ? uPort : offPort, &u32Value, cb);
264 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfIn), a);
265 if (rcStrict == VERR_IOM_IOPORT_UNUSED)
266 {
267 u32Value = UINT32_MAX;
268 rcStrict = VINF_SUCCESS;
269 }
270 if (IOM_SUCCESS(rcStrict))
271 {
272 switch (cb)
273 {
274 case 4: *(uint32_t *)pvDst = u32Value; pvDst = (uint8_t *)pvDst + 4; break;
275 case 2: *(uint16_t *)pvDst = (uint16_t)u32Value; pvDst = (uint8_t *)pvDst + 2; break;
276 case 1: *(uint8_t *)pvDst = (uint8_t )u32Value; pvDst = (uint8_t *)pvDst + 1; break;
277 default: AssertFailed();
278 }
279 *pcTransfers -= 1;
280 }
281 } while ( *pcTransfers > 0
282 && rcStrict == VINF_SUCCESS);
283 }
284 PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo));
285
286#ifdef VBOX_WITH_STATISTICS
287# ifndef IN_RING3
288 if (rcStrict == VINF_IOM_R3_IOPORT_READ)
289 STAM_COUNTER_INC(&pStats->InRZToR3);
290 else
291# endif
292 {
293 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(In));
294 STAM_COUNTER_INC(&iomIoPortGetStats(pVM, pRegEntry, 0)->Total);
295 }
296#endif
297 Log3(("IOMIOPortReadStr: uPort=%RTiop pvDst=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=%Rrc\n",
298 uPort, pvDst, pcTransfers, cRequestedTransfers, *pcTransfers, cb, VBOXSTRICTRC_VAL(rcStrict)));
299 }
300#ifndef IN_RING3
301 else
302 STAM_COUNTER_INC(&pStats->InRZToR3);
303#endif
304 return rcStrict;
305 }
306
307 /*
308 * Ok, no handler for this port.
309 */
310 IOM_UNLOCK_SHARED(pVM);
311 *pcTransfers = 0;
312 memset(pvDst, 0xff, cRequestedTransfers * cb);
313 Log3(("IOMIOPortReadStr: uPort=%RTiop (unused) pvDst=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=VINF_SUCCESS\n",
314 uPort, pvDst, pcTransfers, cRequestedTransfers, *pcTransfers, cb));
315 return VINF_SUCCESS;
316}
317
318
319#ifndef IN_RING3
320/**
321 * Defers a pending I/O port write to ring-3.
322 *
323 * @returns VINF_IOM_R3_IOPORT_COMMIT_WRITE
324 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
325 * @param Port The port to write to.
326 * @param u32Value The value to write.
327 * @param cbValue The size of the value (1, 2, 4).
328 */
329static VBOXSTRICTRC iomIOPortRing3WritePending(PVMCPU pVCpu, RTIOPORT Port, uint32_t u32Value, size_t cbValue)
330{
331 Log5(("iomIOPortRing3WritePending: %#x LB %u -> %RTiop\n", u32Value, cbValue, Port));
332 AssertReturn(pVCpu->iom.s.PendingIOPortWrite.cbValue == 0, VERR_IOM_IOPORT_IPE_1);
333 pVCpu->iom.s.PendingIOPortWrite.IOPort = Port;
334 pVCpu->iom.s.PendingIOPortWrite.u32Value = u32Value;
335 pVCpu->iom.s.PendingIOPortWrite.cbValue = (uint32_t)cbValue;
336 VMCPU_FF_SET(pVCpu, VMCPU_FF_IOM);
337 return VINF_IOM_R3_IOPORT_COMMIT_WRITE;
338}
339#endif
340
341
342/**
343 * Writes to an I/O port register.
344 *
345 * @returns Strict VBox status code. Informational status codes other than the one documented
346 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
347 * @retval VINF_SUCCESS Success.
348 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
349 * status code must be passed on to EM.
350 * @retval VINF_IOM_R3_IOPORT_WRITE Defer the write to ring-3. (R0/RC only)
351 *
352 * @param pVM The cross context VM structure.
353 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
354 * @param Port The port to write to.
355 * @param u32Value The value to write.
356 * @param cbValue The size of the register to read in bytes. 1, 2 or 4 bytes.
357 */
358VMMDECL(VBOXSTRICTRC) IOMIOPortWrite(PVMCC pVM, PVMCPU pVCpu, RTIOPORT Port, uint32_t u32Value, size_t cbValue)
359{
360 STAM_COUNTER_INC(&pVM->iom.s.StatIoPortOut);
361#ifndef IN_RING3
362 Assert(pVCpu->iom.s.PendingIOPortWrite.cbValue == 0);
363#endif
364
365 /* For lookups we need to share lock IOM. */
366 int rc2 = IOM_LOCK_SHARED(pVM);
367#ifndef IN_RING3
368 if (rc2 == VERR_SEM_BUSY)
369 return iomIOPortRing3WritePending(pVCpu, Port, u32Value, cbValue);
370#endif
371 AssertRC(rc2);
372
373 /*
374 * Get the entry for the current context.
375 */
376 uint16_t offPort;
377 CTX_SUFF(PIOMIOPORTENTRY) pRegEntry = iomIoPortGetEntry(pVM, Port, &offPort, &pVCpu->iom.s.idxIoPortLastWrite);
378 if (pRegEntry)
379 {
380#ifdef VBOX_WITH_STATISTICS
381 PIOMIOPORTSTATSENTRY pStats = iomIoPortGetStats(pVM, pRegEntry, offPort);
382#endif
383
384 /*
385 * Found an entry, get the data so we can leave the IOM lock.
386 */
387 uint16_t const fFlags = pRegEntry->fFlags;
388 PFNIOMIOPORTNEWOUT pfnOutCallback = pRegEntry->pfnOutCallback;
389 PPDMDEVINS pDevIns = pRegEntry->pDevIns;
390#ifndef IN_RING3
391 if ( pfnOutCallback
392 && pDevIns
393 && pRegEntry->cPorts > 0)
394 { /* likely */ }
395 else
396 {
397 IOM_UNLOCK_SHARED(pVM);
398 STAM_COUNTER_INC(&pStats->OutRZToR3);
399 return iomIOPortRing3WritePending(pVCpu, Port, u32Value, cbValue);
400 }
401#endif
402 void *pvUser = pRegEntry->pvUser;
403 IOM_UNLOCK_SHARED(pVM);
404 AssertPtr(pDevIns);
405 AssertPtr(pfnOutCallback);
406
407 /*
408 * Call the device.
409 */
410 VBOXSTRICTRC rcStrict = PDMCritSectEnter(pDevIns->CTX_SUFF(pCritSectRo), VINF_IOM_R3_IOPORT_WRITE);
411 if (rcStrict == VINF_SUCCESS)
412 {
413 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfOut), a);
414 rcStrict = pfnOutCallback(pDevIns, pvUser, fFlags & IOM_IOPORT_F_ABS ? Port : offPort, u32Value, (unsigned)cbValue);
415 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfOut), a);
416
417 PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo));
418
419#ifdef VBOX_WITH_STATISTICS
420# ifndef IN_RING3
421 if (rcStrict != VINF_IOM_R3_IOPORT_WRITE)
422# endif
423 {
424 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Out));
425 STAM_COUNTER_INC(&iomIoPortGetStats(pVM, pRegEntry, 0)->Total);
426 }
427#endif
428 Log3(("IOMIOPortWrite: Port=%RTiop u32=%08RX32 cb=%d rc=%Rrc\n", Port, u32Value, cbValue, VBOXSTRICTRC_VAL(rcStrict)));
429 }
430#ifndef IN_RING3
431 if (rcStrict == VINF_IOM_R3_IOPORT_WRITE)
432 {
433 STAM_COUNTER_INC(&pStats->OutRZToR3);
434 return iomIOPortRing3WritePending(pVCpu, Port, u32Value, cbValue);
435 }
436#endif
437 return rcStrict;
438 }
439
440 /*
441 * Ok, no handler for that port.
442 */
443 IOM_UNLOCK_SHARED(pVM);
444 Log3(("IOMIOPortWrite: Port=%RTiop u32=%08RX32 cb=%d nop\n", Port, u32Value, cbValue));
445 return VINF_SUCCESS;
446}
447
448
449/**
450 * Writes the string buffer of an I/O port register.
451 *
452 * @returns Strict VBox status code. Informational status codes other than the one documented
453 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
454 * @retval VINF_SUCCESS Success or no string I/O callback in
455 * this context.
456 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
457 * status code must be passed on to EM.
458 * @retval VINF_IOM_R3_IOPORT_WRITE Defer the write to ring-3. (R0/RC only)
459 *
460 * @param pVM The cross context VM structure.
461 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
462 * @param uPort The port to write to.
463 * @param pvSrc The guest page to read from.
464 * @param pcTransfers Pointer to the number of transfer units to write, on
465 * return remaining transfer units.
466 * @param cb Size of the transfer unit (1, 2 or 4 bytes).
467 */
468VMM_INT_DECL(VBOXSTRICTRC) IOMIOPortWriteString(PVMCC pVM, PVMCPU pVCpu, RTIOPORT uPort, void const *pvSrc,
469 uint32_t *pcTransfers, unsigned cb)
470{
471 STAM_COUNTER_INC(&pVM->iom.s.StatIoPortOutS);
472 Assert(pVCpu->iom.s.PendingIOPortWrite.cbValue == 0);
473 Assert(cb == 1 || cb == 2 || cb == 4);
474
475 /* Take the IOM lock before performing any device I/O. */
476 int rc2 = IOM_LOCK_SHARED(pVM);
477#ifndef IN_RING3
478 if (rc2 == VERR_SEM_BUSY)
479 return VINF_IOM_R3_IOPORT_WRITE;
480#endif
481 AssertRC(rc2);
482
483 const uint32_t cRequestedTransfers = *pcTransfers;
484 Assert(cRequestedTransfers > 0);
485
486 /*
487 * Get the entry for the current context.
488 */
489 uint16_t offPort;
490 CTX_SUFF(PIOMIOPORTENTRY) pRegEntry = iomIoPortGetEntry(pVM, uPort, &offPort, &pVCpu->iom.s.idxIoPortLastWriteStr);
491 if (pRegEntry)
492 {
493#ifdef VBOX_WITH_STATISTICS
494 PIOMIOPORTSTATSENTRY pStats = iomIoPortGetStats(pVM, pRegEntry, offPort);
495#endif
496
497 /*
498 * Found an entry, get the data so we can leave the IOM lock.
499 */
500 uint16_t const fFlags = pRegEntry->fFlags;
501 PFNIOMIOPORTNEWOUTSTRING pfnOutStrCallback = pRegEntry->pfnOutStrCallback;
502 PFNIOMIOPORTNEWOUT pfnOutCallback = pRegEntry->pfnOutCallback;
503 PPDMDEVINS pDevIns = pRegEntry->pDevIns;
504#ifndef IN_RING3
505 if ( pfnOutCallback
506 && pDevIns
507 && pRegEntry->cPorts > 0)
508 { /* likely */ }
509 else
510 {
511 IOM_UNLOCK_SHARED(pVM);
512 STAM_COUNTER_INC(&pStats->OutRZToR3);
513 return VINF_IOM_R3_IOPORT_WRITE;
514 }
515#endif
516 void *pvUser = pRegEntry->pvUser;
517 IOM_UNLOCK_SHARED(pVM);
518 AssertPtr(pDevIns);
519 AssertPtr(pfnOutCallback);
520
521 /*
522 * Call the device.
523 */
524 VBOXSTRICTRC rcStrict = PDMCritSectEnter(pDevIns->CTX_SUFF(pCritSectRo), VINF_IOM_R3_IOPORT_WRITE);
525 if (rcStrict == VINF_SUCCESS)
526 {
527 /*
528 * First using string I/O if possible.
529 */
530 if (pfnOutStrCallback)
531 {
532 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfOut), a);
533 rcStrict = pfnOutStrCallback(pDevIns, pvUser, fFlags & IOM_IOPORT_F_ABS ? uPort : offPort,
534 (uint8_t const *)pvSrc, pcTransfers, cb);
535 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfOut), a);
536 }
537
538 /*
539 * Then doing the single I/O fallback.
540 */
541 if ( *pcTransfers > 0
542 && rcStrict == VINF_SUCCESS)
543 {
544 pvSrc = (uint8_t *)pvSrc + (cRequestedTransfers - *pcTransfers) * cb;
545 do
546 {
547 uint32_t u32Value;
548 switch (cb)
549 {
550 case 4: u32Value = *(uint32_t *)pvSrc; pvSrc = (uint8_t const *)pvSrc + 4; break;
551 case 2: u32Value = *(uint16_t *)pvSrc; pvSrc = (uint8_t const *)pvSrc + 2; break;
552 case 1: u32Value = *(uint8_t *)pvSrc; pvSrc = (uint8_t const *)pvSrc + 1; break;
553 default: AssertFailed(); u32Value = UINT32_MAX;
554 }
555 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfOut), a);
556 rcStrict = pfnOutCallback(pDevIns, pvUser, fFlags & IOM_IOPORT_F_ABS ? uPort : offPort, u32Value, cb);
557 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfOut), a);
558 if (IOM_SUCCESS(rcStrict))
559 *pcTransfers -= 1;
560 } while ( *pcTransfers > 0
561 && rcStrict == VINF_SUCCESS);
562 }
563
564 PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo));
565
566#ifdef VBOX_WITH_STATISTICS
567# ifndef IN_RING3
568 if (rcStrict == VINF_IOM_R3_IOPORT_WRITE)
569 STAM_COUNTER_INC(&pStats->OutRZToR3);
570 else
571# endif
572 {
573 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Out));
574 STAM_COUNTER_INC(&iomIoPortGetStats(pVM, pRegEntry, 0)->Total);
575 }
576#endif
577 Log3(("IOMIOPortWriteStr: uPort=%RTiop pvSrc=%p pcTransfer=%p:{%#x->%#x} cb=%d rcStrict=%Rrc\n",
578 uPort, pvSrc, pcTransfers, cRequestedTransfers, *pcTransfers, cb, VBOXSTRICTRC_VAL(rcStrict)));
579 }
580#ifndef IN_RING3
581 else
582 STAM_COUNTER_INC(&pStats->OutRZToR3);
583#endif
584 return rcStrict;
585 }
586
587 /*
588 * Ok, no handler for this port.
589 */
590 IOM_UNLOCK_SHARED(pVM);
591 *pcTransfers = 0;
592 Log3(("IOMIOPortWriteStr: uPort=%RTiop (unused) pvSrc=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=VINF_SUCCESS\n",
593 uPort, pvSrc, pcTransfers, cRequestedTransfers, *pcTransfers, cb));
594 return VINF_SUCCESS;
595}
596
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use