VirtualBox

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

Last change on this file since 92561 was 90426, checked in by vboxsync, 3 years ago

IOM: Be more careful checking the status of IOM_LOCK_SHARED(). bugref:6695

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