VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use