VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/IOMAllMMIO.cpp@ 48580

Last change on this file since 48580 was 48410, checked in by vboxsync, 11 years ago

IOMMMIOPhysHandler: Don't sit on the IOM lock.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 92.4 KB
Line 
1/* $Id: IOMAllMMIO.cpp 48410 2013-09-10 14:22:51Z vboxsync $ */
2/** @file
3 * IOM - Input / Output Monitor - Any Context, MMIO & String I/O.
4 */
5
6/*
7 * Copyright (C) 2006-2013 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
23#include <VBox/vmm/iom.h>
24#include <VBox/vmm/cpum.h>
25#include <VBox/vmm/pgm.h>
26#include <VBox/vmm/selm.h>
27#include <VBox/vmm/mm.h>
28#include <VBox/vmm/em.h>
29#include <VBox/vmm/pgm.h>
30#include <VBox/vmm/trpm.h>
31#if defined(IEM_VERIFICATION_MODE) && defined(IN_RING3)
32# include <VBox/vmm/iem.h>
33#endif
34#include "IOMInternal.h"
35#include <VBox/vmm/vm.h>
36#include <VBox/vmm/vmm.h>
37#include <VBox/vmm/hm.h>
38#include "IOMInline.h"
39
40#include <VBox/dis.h>
41#include <VBox/disopcode.h>
42#include <VBox/vmm/pdmdev.h>
43#include <VBox/param.h>
44#include <VBox/err.h>
45#include <iprt/assert.h>
46#include <VBox/log.h>
47#include <iprt/asm.h>
48#include <iprt/string.h>
49
50
51/*******************************************************************************
52* Global Variables *
53*******************************************************************************/
54
55/**
56 * Array for fast recode of the operand size (1/2/4/8 bytes) to bit shift value.
57 */
58static const unsigned g_aSize2Shift[] =
59{
60 ~0U, /* 0 - invalid */
61 0, /* *1 == 2^0 */
62 1, /* *2 == 2^1 */
63 ~0U, /* 3 - invalid */
64 2, /* *4 == 2^2 */
65 ~0U, /* 5 - invalid */
66 ~0U, /* 6 - invalid */
67 ~0U, /* 7 - invalid */
68 3 /* *8 == 2^3 */
69};
70
71/**
72 * Macro for fast recode of the operand size (1/2/4/8 bytes) to bit shift value.
73 */
74#define SIZE_2_SHIFT(cb) (g_aSize2Shift[cb])
75
76
77/**
78 * Deals with complicated MMIO writes.
79 *
80 * Complicated means unaligned or non-dword/qword sized accesses depending on
81 * the MMIO region's access mode flags.
82 *
83 * @returns Strict VBox status code. Any EM scheduling status code,
84 * VINF_IOM_R3_MMIO_WRITE, VINF_IOM_R3_MMIO_READ_WRITE or
85 * VINF_IOM_R3_MMIO_READ may be returned.
86 *
87 * @param pVM Pointer to the VM.
88 * @param pRange The range to write to.
89 * @param GCPhys The physical address to start writing.
90 * @param pvValue Where to store the value.
91 * @param cbValue The size of the value to write.
92 */
93static VBOXSTRICTRC iomMMIODoComplicatedWrite(PVM pVM, PIOMMMIORANGE pRange, RTGCPHYS GCPhys, void const *pvValue, unsigned cbValue)
94{
95 AssertReturn( (pRange->fFlags & IOMMMIO_FLAGS_WRITE_MODE) != IOMMMIO_FLAGS_WRITE_PASSTHRU
96 || (pRange->fFlags & IOMMMIO_FLAGS_WRITE_MODE) <= IOMMMIO_FLAGS_WRITE_DWORD_QWORD_READ_MISSING,
97 VERR_IOM_MMIO_IPE_1);
98 AssertReturn(cbValue != 0 && cbValue <= 16, VERR_IOM_MMIO_IPE_2);
99 RTGCPHYS const GCPhysStart = GCPhys; NOREF(GCPhysStart);
100 bool const fReadMissing = (pRange->fFlags & IOMMMIO_FLAGS_WRITE_MODE) == IOMMMIO_FLAGS_WRITE_DWORD_READ_MISSING
101 || (pRange->fFlags & IOMMMIO_FLAGS_WRITE_MODE) == IOMMMIO_FLAGS_WRITE_DWORD_QWORD_READ_MISSING;
102
103 /*
104 * Do debug stop if requested.
105 */
106 int rc = VINF_SUCCESS; NOREF(pVM);
107#ifdef VBOX_STRICT
108 if (pRange->fFlags & IOMMMIO_FLAGS_DBGSTOP_ON_COMPLICATED_WRITE)
109 {
110# ifdef IN_RING3
111 rc = DBGFR3EventSrc(pVM, DBGFEVENT_DEV_STOP, RT_SRC_POS,
112 "Complicated write %#x byte at %RGp to %s\n", cbValue, GCPhys, R3STRING(pRange->pszDesc));
113 if (rc == VERR_DBGF_NOT_ATTACHED)
114 rc = VINF_SUCCESS;
115# else
116 return VINF_IOM_R3_MMIO_WRITE;
117# endif
118 }
119#endif
120
121 /*
122 * Check if we should ignore the write.
123 */
124 if ((pRange->fFlags & IOMMMIO_FLAGS_WRITE_MODE) == IOMMMIO_FLAGS_WRITE_ONLY_DWORD)
125 {
126 Assert(cbValue != 4 || (GCPhys & 3));
127 return VINF_SUCCESS;
128 }
129 if ((pRange->fFlags & IOMMMIO_FLAGS_WRITE_MODE) == IOMMMIO_FLAGS_WRITE_ONLY_DWORD_QWORD)
130 {
131 Assert((cbValue != 4 && cbValue != 8) || (GCPhys & (cbValue - 1)));
132 return VINF_SUCCESS;
133 }
134
135 /*
136 * Split and conquer.
137 */
138 for (;;)
139 {
140 unsigned const offAccess = GCPhys & 3;
141 unsigned cbThisPart = 4 - offAccess;
142 if (cbThisPart > cbValue)
143 cbThisPart = cbValue;
144
145 /*
146 * Get the missing bits (if any).
147 */
148 uint32_t u32MissingValue = 0;
149 if (fReadMissing && cbThisPart != 4)
150 {
151 int rc2 = pRange->CTX_SUFF(pfnReadCallback)(pRange->CTX_SUFF(pDevIns), pRange->CTX_SUFF(pvUser),
152 GCPhys & ~(RTGCPHYS)3, &u32MissingValue, sizeof(u32MissingValue));
153 switch (rc2)
154 {
155 case VINF_SUCCESS:
156 break;
157 case VINF_IOM_MMIO_UNUSED_FF:
158 u32MissingValue = UINT32_C(0xffffffff);
159 break;
160 case VINF_IOM_MMIO_UNUSED_00:
161 u32MissingValue = 0;
162 break;
163 case VINF_IOM_R3_MMIO_READ:
164 case VINF_IOM_R3_MMIO_READ_WRITE:
165 case VINF_IOM_R3_MMIO_WRITE:
166 /** @todo What if we've split a transfer and already read
167 * something? Since writes generally have sideeffects we
168 * could be kind of screwed here...
169 *
170 * Fix: Save the current state and resume it in ring-3. Requires EM to not go
171 * to REM for MMIO accesses (like may currently do). */
172
173 LogFlow(("iomMMIODoComplicatedWrite: GCPhys=%RGp GCPhysStart=%RGp cbValue=%u rc=%Rrc [read]\n", GCPhys, GCPhysStart, cbValue, rc2));
174 return rc2;
175 default:
176 if (RT_FAILURE(rc2))
177 {
178 Log(("iomMMIODoComplicatedWrite: GCPhys=%RGp GCPhysStart=%RGp cbValue=%u rc=%Rrc [read]\n", GCPhys, GCPhysStart, cbValue, rc2));
179 return rc2;
180 }
181 AssertMsgReturn(rc2 >= VINF_EM_FIRST && rc2 <= VINF_EM_LAST, ("%Rrc\n", rc2), VERR_IPE_UNEXPECTED_INFO_STATUS);
182 if (rc == VINF_SUCCESS || rc2 < rc)
183 rc = rc2;
184 break;
185 }
186 }
187
188 /*
189 * Merge missing and given bits.
190 */
191 uint32_t u32GivenMask;
192 uint32_t u32GivenValue;
193 switch (cbThisPart)
194 {
195 case 1:
196 u32GivenValue = *(uint8_t const *)pvValue;
197 u32GivenMask = UINT32_C(0x000000ff);
198 break;
199 case 2:
200 u32GivenValue = *(uint16_t const *)pvValue;
201 u32GivenMask = UINT32_C(0x0000ffff);
202 break;
203 case 3:
204 u32GivenValue = RT_MAKE_U32_FROM_U8(((uint8_t const *)pvValue)[0], ((uint8_t const *)pvValue)[1],
205 ((uint8_t const *)pvValue)[2], 0);
206 u32GivenMask = UINT32_C(0x00ffffff);
207 break;
208 case 4:
209 u32GivenValue = *(uint32_t const *)pvValue;
210 u32GivenMask = UINT32_C(0xffffffff);
211 break;
212 default:
213 AssertFailedReturn(VERR_IOM_MMIO_IPE_3);
214 }
215 if (offAccess)
216 {
217 u32GivenValue <<= offAccess * 8;
218 u32GivenMask <<= offAccess * 8;
219 }
220
221 uint32_t u32Value = (u32MissingValue & ~u32GivenMask)
222 | (u32GivenValue & u32GivenMask);
223
224 /*
225 * Do DWORD write to the device.
226 */
227 int rc2 = pRange->CTX_SUFF(pfnWriteCallback)(pRange->CTX_SUFF(pDevIns), pRange->CTX_SUFF(pvUser),
228 GCPhys & ~(RTGCPHYS)3, &u32Value, sizeof(u32Value));
229 switch (rc2)
230 {
231 case VINF_SUCCESS:
232 break;
233 case VINF_IOM_R3_MMIO_READ:
234 case VINF_IOM_R3_MMIO_READ_WRITE:
235 case VINF_IOM_R3_MMIO_WRITE:
236 /** @todo What if we've split a transfer and already read
237 * something? Since reads can have sideeffects we could be
238 * kind of screwed here...
239 *
240 * Fix: Save the current state and resume it in ring-3. Requires EM to not go
241 * to REM for MMIO accesses (like may currently do). */
242 LogFlow(("iomMMIODoComplicatedWrite: GCPhys=%RGp GCPhysStart=%RGp cbValue=%u rc=%Rrc [write]\n", GCPhys, GCPhysStart, cbValue, rc2));
243 return rc2;
244 default:
245 if (RT_FAILURE(rc2))
246 {
247 Log(("iomMMIODoComplicatedWrite: GCPhys=%RGp GCPhysStart=%RGp cbValue=%u rc=%Rrc [write]\n", GCPhys, GCPhysStart, cbValue, rc2));
248 return rc2;
249 }
250 AssertMsgReturn(rc2 >= VINF_EM_FIRST && rc2 <= VINF_EM_LAST, ("%Rrc\n", rc2), VERR_IPE_UNEXPECTED_INFO_STATUS);
251 if (rc == VINF_SUCCESS || rc2 < rc)
252 rc = rc2;
253 break;
254 }
255
256 /*
257 * Advance.
258 */
259 cbValue -= cbThisPart;
260 if (!cbValue)
261 break;
262 GCPhys += cbThisPart;
263 pvValue = (uint8_t const *)pvValue + cbThisPart;
264 }
265
266 return rc;
267}
268
269
270
271
272/**
273 * Wrapper which does the write and updates range statistics when such are enabled.
274 * @warning RT_SUCCESS(rc=VINF_IOM_R3_MMIO_WRITE) is TRUE!
275 */
276static int iomMMIODoWrite(PVM pVM, PVMCPU pVCpu, PIOMMMIORANGE pRange, RTGCPHYS GCPhysFault, const void *pvData, unsigned cb)
277{
278#ifdef VBOX_WITH_STATISTICS
279 int rcSem = IOM_LOCK_SHARED(pVM);
280 if (rcSem == VERR_SEM_BUSY)
281 return VINF_IOM_R3_MMIO_WRITE;
282 PIOMMMIOSTATS pStats = iomMmioGetStats(pVM, pVCpu, GCPhysFault, pRange);
283 if (!pStats)
284# ifdef IN_RING3
285 return VERR_NO_MEMORY;
286# else
287 return VINF_IOM_R3_MMIO_WRITE;
288# endif
289 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfWrite), a);
290#endif
291
292 VBOXSTRICTRC rc;
293 if (RT_LIKELY(pRange->CTX_SUFF(pfnWriteCallback)))
294 {
295 if ( (cb == 4 && !(GCPhysFault & 3))
296 || (pRange->fFlags & IOMMMIO_FLAGS_WRITE_MODE) == IOMMMIO_FLAGS_WRITE_PASSTHRU
297 || (cb == 8 && !(GCPhysFault & 7) && IOMMMIO_DOES_WRITE_MODE_ALLOW_QWORD(pRange->fFlags)) )
298 rc = pRange->CTX_SUFF(pfnWriteCallback)(pRange->CTX_SUFF(pDevIns), pRange->CTX_SUFF(pvUser),
299 GCPhysFault, (void *)pvData, cb); /** @todo fix const!! */
300 else
301 rc = iomMMIODoComplicatedWrite(pVM, pRange, GCPhysFault, pvData, cb);
302 }
303 else
304 rc = VINF_SUCCESS;
305
306 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfWrite), a);
307 STAM_COUNTER_INC(&pStats->Accesses);
308 return VBOXSTRICTRC_TODO(rc);
309}
310
311
312/**
313 * Deals with complicated MMIO reads.
314 *
315 * Complicatd means unaligned or non-dword/qword align accesses depending on
316 * the MMIO region's access mode flags.
317 *
318 * @returns Strict VBox status code. Any EM scheduling status code,
319 * VINF_IOM_R3_MMIO_READ, VINF_IOM_R3_MMIO_READ_WRITE or
320 * VINF_IOM_R3_MMIO_WRITE may be returned.
321 *
322 * @param pVM Pointer to the VM.
323 * @param pRange The range to read from.
324 * @param GCPhys The physical address to start reading.
325 * @param pvValue Where to store the value.
326 * @param cbValue The size of the value to read.
327 */
328static VBOXSTRICTRC iomMMIODoComplicatedRead(PVM pVM, PIOMMMIORANGE pRange, RTGCPHYS GCPhys, void *pvValue, unsigned cbValue)
329{
330 AssertReturn( (pRange->fFlags & IOMMMIO_FLAGS_READ_MODE) == IOMMMIO_FLAGS_READ_DWORD
331 || (pRange->fFlags & IOMMMIO_FLAGS_READ_MODE) == IOMMMIO_FLAGS_READ_DWORD_QWORD,
332 VERR_IOM_MMIO_IPE_1);
333 AssertReturn(cbValue != 0 && cbValue <= 16, VERR_IOM_MMIO_IPE_2);
334 RTGCPHYS const GCPhysStart = GCPhys; NOREF(GCPhysStart);
335
336 /*
337 * Do debug stop if requested.
338 */
339 int rc = VINF_SUCCESS; NOREF(pVM);
340#ifdef VBOX_STRICT
341 if (pRange->fFlags & IOMMMIO_FLAGS_DBGSTOP_ON_COMPLICATED_READ)
342 {
343# ifdef IN_RING3
344 rc = DBGFR3EventSrc(pVM, DBGFEVENT_DEV_STOP, RT_SRC_POS,
345 "Complicated read %#x byte at %RGp to %s\n", cbValue, GCPhys, R3STRING(pRange->pszDesc));
346 if (rc == VERR_DBGF_NOT_ATTACHED)
347 rc = VINF_SUCCESS;
348# else
349 return VINF_IOM_R3_MMIO_READ;
350# endif
351 }
352#endif
353
354 /*
355 * Split and conquer.
356 */
357 for (;;)
358 {
359 /*
360 * Do DWORD read from the device.
361 */
362 uint32_t u32Value;
363 int rc2 = pRange->CTX_SUFF(pfnReadCallback)(pRange->CTX_SUFF(pDevIns), pRange->CTX_SUFF(pvUser),
364 GCPhys & ~(RTGCPHYS)3, &u32Value, sizeof(u32Value));
365 switch (rc2)
366 {
367 case VINF_SUCCESS:
368 break;
369 case VINF_IOM_MMIO_UNUSED_FF:
370 u32Value = UINT32_C(0xffffffff);
371 break;
372 case VINF_IOM_MMIO_UNUSED_00:
373 u32Value = 0;
374 break;
375 case VINF_IOM_R3_MMIO_READ:
376 case VINF_IOM_R3_MMIO_READ_WRITE:
377 case VINF_IOM_R3_MMIO_WRITE:
378 /** @todo What if we've split a transfer and already read
379 * something? Since reads can have sideeffects we could be
380 * kind of screwed here... */
381 LogFlow(("iomMMIODoComplicatedRead: GCPhys=%RGp GCPhysStart=%RGp cbValue=%u rc=%Rrc\n", GCPhys, GCPhysStart, cbValue, rc2));
382 return rc2;
383 default:
384 if (RT_FAILURE(rc2))
385 {
386 Log(("iomMMIODoComplicatedRead: GCPhys=%RGp GCPhysStart=%RGp cbValue=%u rc=%Rrc\n", GCPhys, GCPhysStart, cbValue, rc2));
387 return rc2;
388 }
389 AssertMsgReturn(rc2 >= VINF_EM_FIRST && rc2 <= VINF_EM_LAST, ("%Rrc\n", rc2), VERR_IPE_UNEXPECTED_INFO_STATUS);
390 if (rc == VINF_SUCCESS || rc2 < rc)
391 rc = rc2;
392 break;
393 }
394 u32Value >>= (GCPhys & 3) * 8;
395
396 /*
397 * Write what we've read.
398 */
399 unsigned cbThisPart = 4 - (GCPhys & 3);
400 if (cbThisPart > cbValue)
401 cbThisPart = cbValue;
402
403 switch (cbThisPart)
404 {
405 case 1:
406 *(uint8_t *)pvValue = (uint8_t)u32Value;
407 break;
408 case 2:
409 *(uint16_t *)pvValue = (uint16_t)u32Value;
410 break;
411 case 3:
412 ((uint8_t *)pvValue)[0] = RT_BYTE1(u32Value);
413 ((uint8_t *)pvValue)[1] = RT_BYTE2(u32Value);
414 ((uint8_t *)pvValue)[2] = RT_BYTE3(u32Value);
415 break;
416 case 4:
417 *(uint32_t *)pvValue = u32Value;
418 break;
419 }
420
421 /*
422 * Advance.
423 */
424 cbValue -= cbThisPart;
425 if (!cbValue)
426 break;
427 GCPhys += cbThisPart;
428 pvValue = (uint8_t *)pvValue + cbThisPart;
429 }
430
431 return rc;
432}
433
434
435/**
436 * Implements VINF_IOM_MMIO_UNUSED_FF.
437 *
438 * @returns VINF_SUCCESS.
439 * @param pvValue Where to store the zeros.
440 * @param cbValue How many bytes to read.
441 */
442static int iomMMIODoReadFFs(void *pvValue, size_t cbValue)
443{
444 switch (cbValue)
445 {
446 case 1: *(uint8_t *)pvValue = UINT8_C(0xff); break;
447 case 2: *(uint16_t *)pvValue = UINT16_C(0xffff); break;
448 case 4: *(uint32_t *)pvValue = UINT32_C(0xffffffff); break;
449 case 8: *(uint64_t *)pvValue = UINT64_C(0xffffffffffffffff); break;
450 default:
451 {
452 uint8_t *pb = (uint8_t *)pvValue;
453 while (cbValue--)
454 *pb++ = UINT8_C(0xff);
455 break;
456 }
457 }
458 return VINF_SUCCESS;
459}
460
461
462/**
463 * Implements VINF_IOM_MMIO_UNUSED_00.
464 *
465 * @returns VINF_SUCCESS.
466 * @param pvValue Where to store the zeros.
467 * @param cbValue How many bytes to read.
468 */
469static int iomMMIODoRead00s(void *pvValue, size_t cbValue)
470{
471 switch (cbValue)
472 {
473 case 1: *(uint8_t *)pvValue = UINT8_C(0x00); break;
474 case 2: *(uint16_t *)pvValue = UINT16_C(0x0000); break;
475 case 4: *(uint32_t *)pvValue = UINT32_C(0x00000000); break;
476 case 8: *(uint64_t *)pvValue = UINT64_C(0x0000000000000000); break;
477 default:
478 {
479 uint8_t *pb = (uint8_t *)pvValue;
480 while (cbValue--)
481 *pb++ = UINT8_C(0x00);
482 break;
483 }
484 }
485 return VINF_SUCCESS;
486}
487
488
489/**
490 * Wrapper which does the read and updates range statistics when such are enabled.
491 */
492DECLINLINE(int) iomMMIODoRead(PVM pVM, PVMCPU pVCpu, PIOMMMIORANGE pRange, RTGCPHYS GCPhys, void *pvValue, unsigned cbValue)
493{
494#ifdef VBOX_WITH_STATISTICS
495 int rcSem = IOM_LOCK_SHARED(pVM);
496 if (rcSem == VERR_SEM_BUSY)
497 return VINF_IOM_R3_MMIO_READ;
498 PIOMMMIOSTATS pStats = iomMmioGetStats(pVM, pVCpu, GCPhys, pRange);
499 if (!pStats)
500# ifdef IN_RING3
501 return VERR_NO_MEMORY;
502# else
503 return VINF_IOM_R3_MMIO_READ;
504# endif
505 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfRead), a);
506#endif
507
508 VBOXSTRICTRC rc;
509 if (RT_LIKELY(pRange->CTX_SUFF(pfnReadCallback)))
510 {
511 if ( ( cbValue == 4
512 && !(GCPhys & 3))
513 || (pRange->fFlags & IOMMMIO_FLAGS_READ_MODE) == IOMMMIO_FLAGS_READ_PASSTHRU
514 || ( cbValue == 8
515 && !(GCPhys & 7)
516 && (pRange->fFlags & IOMMMIO_FLAGS_READ_MODE) == IOMMMIO_FLAGS_READ_DWORD_QWORD ) )
517 rc = pRange->CTX_SUFF(pfnReadCallback)(pRange->CTX_SUFF(pDevIns), pRange->CTX_SUFF(pvUser), GCPhys, pvValue, cbValue);
518 else
519 rc = iomMMIODoComplicatedRead(pVM, pRange, GCPhys, pvValue, cbValue);
520 }
521 else
522 rc = VINF_IOM_MMIO_UNUSED_FF;
523 if (rc != VINF_SUCCESS)
524 {
525 switch (VBOXSTRICTRC_VAL(rc))
526 {
527 case VINF_IOM_MMIO_UNUSED_FF: rc = iomMMIODoReadFFs(pvValue, cbValue); break;
528 case VINF_IOM_MMIO_UNUSED_00: rc = iomMMIODoRead00s(pvValue, cbValue); break;
529 }
530 }
531
532 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfRead), a);
533 STAM_COUNTER_INC(&pStats->Accesses);
534 return VBOXSTRICTRC_VAL(rc);
535}
536
537
538/**
539 * Internal - statistics only.
540 */
541DECLINLINE(void) iomMMIOStatLength(PVM pVM, unsigned cb)
542{
543#ifdef VBOX_WITH_STATISTICS
544 switch (cb)
545 {
546 case 1:
547 STAM_COUNTER_INC(&pVM->iom.s.StatRZMMIO1Byte);
548 break;
549 case 2:
550 STAM_COUNTER_INC(&pVM->iom.s.StatRZMMIO2Bytes);
551 break;
552 case 4:
553 STAM_COUNTER_INC(&pVM->iom.s.StatRZMMIO4Bytes);
554 break;
555 case 8:
556 STAM_COUNTER_INC(&pVM->iom.s.StatRZMMIO8Bytes);
557 break;
558 default:
559 /* No way. */
560 AssertMsgFailed(("Invalid data length %d\n", cb));
561 break;
562 }
563#else
564 NOREF(pVM); NOREF(cb);
565#endif
566}
567
568
569/**
570 * MOV reg, mem (read)
571 * MOVZX reg, mem (read)
572 * MOVSX reg, mem (read)
573 *
574 * @returns VBox status code.
575 *
576 * @param pVM The virtual machine.
577 * @param pVCpu Pointer to the virtual CPU structure of the caller.
578 * @param pRegFrame Pointer to CPUMCTXCORE guest registers structure.
579 * @param pCpu Disassembler CPU state.
580 * @param pRange Pointer MMIO range.
581 * @param GCPhysFault The GC physical address corresponding to pvFault.
582 */
583static int iomInterpretMOVxXRead(PVM pVM, PVMCPU pVCpu, PCPUMCTXCORE pRegFrame, PDISCPUSTATE pCpu,
584 PIOMMMIORANGE pRange, RTGCPHYS GCPhysFault)
585{
586 Assert(pRange->CTX_SUFF(pfnReadCallback) || !pRange->pfnReadCallbackR3);
587
588 /*
589 * Get the data size from parameter 2,
590 * and call the handler function to get the data.
591 */
592 unsigned cb = DISGetParamSize(pCpu, &pCpu->Param2);
593 AssertMsg(cb > 0 && cb <= sizeof(uint64_t), ("cb=%d\n", cb));
594
595 uint64_t u64Data = 0;
596 int rc = iomMMIODoRead(pVM, pVCpu, pRange, GCPhysFault, &u64Data, cb);
597 if (rc == VINF_SUCCESS)
598 {
599 /*
600 * Do sign extension for MOVSX.
601 */
602 /** @todo checkup MOVSX implementation! */
603 if (pCpu->pCurInstr->uOpcode == OP_MOVSX)
604 {
605 if (cb == 1)
606 {
607 /* DWORD <- BYTE */
608 int64_t iData = (int8_t)u64Data;
609 u64Data = (uint64_t)iData;
610 }
611 else
612 {
613 /* DWORD <- WORD */
614 int64_t iData = (int16_t)u64Data;
615 u64Data = (uint64_t)iData;
616 }
617 }
618
619 /*
620 * Store the result to register (parameter 1).
621 */
622 bool fRc = iomSaveDataToReg(pCpu, &pCpu->Param1, pRegFrame, u64Data);
623 AssertMsg(fRc, ("Failed to store register value!\n")); NOREF(fRc);
624 }
625
626 if (rc == VINF_SUCCESS)
627 iomMMIOStatLength(pVM, cb);
628 return rc;
629}
630
631
632/**
633 * MOV mem, reg|imm (write)
634 *
635 * @returns VBox status code.
636 *
637 * @param pVM The virtual machine.
638 * @param pVCpu Pointer to the virtual CPU structure of the caller.
639 * @param pRegFrame Pointer to CPUMCTXCORE guest registers structure.
640 * @param pCpu Disassembler CPU state.
641 * @param pRange Pointer MMIO range.
642 * @param GCPhysFault The GC physical address corresponding to pvFault.
643 */
644static int iomInterpretMOVxXWrite(PVM pVM, PVMCPU pVCpu, PCPUMCTXCORE pRegFrame, PDISCPUSTATE pCpu,
645 PIOMMMIORANGE pRange, RTGCPHYS GCPhysFault)
646{
647 Assert(pRange->CTX_SUFF(pfnWriteCallback) || !pRange->pfnWriteCallbackR3);
648
649 /*
650 * Get data to write from second parameter,
651 * and call the callback to write it.
652 */
653 unsigned cb = 0;
654 uint64_t u64Data = 0;
655 bool fRc = iomGetRegImmData(pCpu, &pCpu->Param2, pRegFrame, &u64Data, &cb);
656 AssertMsg(fRc, ("Failed to get reg/imm port number!\n")); NOREF(fRc);
657
658 int rc = iomMMIODoWrite(pVM, pVCpu, pRange, GCPhysFault, &u64Data, cb);
659 if (rc == VINF_SUCCESS)
660 iomMMIOStatLength(pVM, cb);
661 return rc;
662}
663
664
665/** Wrapper for reading virtual memory. */
666DECLINLINE(int) iomRamRead(PVMCPU pVCpu, void *pDest, RTGCPTR GCSrc, uint32_t cb)
667{
668 /* Note: This will fail in R0 or RC if it hits an access handler. That
669 isn't a problem though since the operation can be restarted in REM. */
670#ifdef IN_RC
671 NOREF(pVCpu);
672 int rc = MMGCRamReadNoTrapHandler(pDest, (void *)(uintptr_t)GCSrc, cb);
673 /* Page may be protected and not directly accessible. */
674 if (rc == VERR_ACCESS_DENIED)
675 rc = VINF_IOM_R3_IOPORT_WRITE;
676 return rc;
677#else
678 return PGMPhysReadGCPtr(pVCpu, pDest, GCSrc, cb);
679#endif
680}
681
682
683/** Wrapper for writing virtual memory. */
684DECLINLINE(int) iomRamWrite(PVMCPU pVCpu, PCPUMCTXCORE pCtxCore, RTGCPTR GCPtrDst, void *pvSrc, uint32_t cb)
685{
686 /** @todo Need to update PGMVerifyAccess to take access handlers into account for Ring-0 and
687 * raw mode code. Some thought needs to be spent on theoretical concurrency issues as
688 * as well since we're not behind the pgm lock and handler may change between calls.
689 *
690 * PGMPhysInterpretedWriteNoHandlers/PGMPhysWriteGCPtr may mess up
691 * the state of some shadowed structures. */
692#if defined(IN_RING0) || defined(IN_RC)
693 return PGMPhysInterpretedWriteNoHandlers(pVCpu, pCtxCore, GCPtrDst, pvSrc, cb, false /*fRaiseTrap*/);
694#else
695 NOREF(pCtxCore);
696 return PGMPhysWriteGCPtr(pVCpu, GCPtrDst, pvSrc, cb);
697#endif
698}
699
700
701#if defined(IOM_WITH_MOVS_SUPPORT) && 0 /* locking prevents this from working. has buggy ecx handling. */
702/**
703 * [REP] MOVSB
704 * [REP] MOVSW
705 * [REP] MOVSD
706 *
707 * Restricted implementation.
708 *
709 *
710 * @returns VBox status code.
711 *
712 * @param pVM The virtual machine.
713 * @param uErrorCode CPU Error code.
714 * @param pRegFrame Trap register frame.
715 * @param GCPhysFault The GC physical address corresponding to pvFault.
716 * @param pCpu Disassembler CPU state.
717 * @param pRange Pointer MMIO range.
718 * @param ppStat Which sub-sample to attribute this call to.
719 */
720static int iomInterpretMOVS(PVM pVM, bool fWriteAccess, PCPUMCTXCORE pRegFrame, RTGCPHYS GCPhysFault, PDISCPUSTATE pCpu, PIOMMMIORANGE pRange,
721 PSTAMPROFILE *ppStat)
722{
723 /*
724 * We do not support segment prefixes or REPNE.
725 */
726 if (pCpu->fPrefix & (DISPREFIX_SEG | DISPREFIX_REPNE))
727 return VINF_IOM_R3_MMIO_READ_WRITE; /** @todo -> interpret whatever. */
728
729 PVMCPU pVCpu = VMMGetCpu(pVM);
730
731 /*
732 * Get bytes/words/dwords/qword count to copy.
733 */
734 uint32_t cTransfers = 1;
735 if (pCpu->fPrefix & DISPREFIX_REP)
736 {
737#ifndef IN_RC
738 if ( CPUMIsGuestIn64BitCode(pVCpu, pRegFrame)
739 && pRegFrame->rcx >= _4G)
740 return VINF_EM_RAW_EMULATE_INSTR;
741#endif
742
743 cTransfers = pRegFrame->ecx;
744 if (SELMGetCpuModeFromSelector(pVM, pRegFrame->eflags, pRegFrame->cs, &pRegFrame->csHid) == DISCPUMODE_16BIT)
745 cTransfers &= 0xffff;
746
747 if (!cTransfers)
748 return VINF_SUCCESS;
749 }
750
751 /* Get the current privilege level. */
752 uint32_t cpl = CPUMGetGuestCPL(pVCpu, pRegFrame);
753
754 /*
755 * Get data size.
756 */
757 unsigned cb = DISGetParamSize(pCpu, &pCpu->Param1);
758 AssertMsg(cb > 0 && cb <= sizeof(uint64_t), ("cb=%d\n", cb));
759 int offIncrement = pRegFrame->eflags.Bits.u1DF ? -(signed)cb : (signed)cb;
760
761#ifdef VBOX_WITH_STATISTICS
762 if (pVM->iom.s.cMovsMaxBytes < (cTransfers << SIZE_2_SHIFT(cb)))
763 pVM->iom.s.cMovsMaxBytes = cTransfers << SIZE_2_SHIFT(cb);
764#endif
765
766/** @todo re-evaluate on page boundaries. */
767
768 RTGCPHYS Phys = GCPhysFault;
769 int rc;
770 if (fWriteAccess)
771 {
772 /*
773 * Write operation: [Mem] -> [MMIO]
774 * ds:esi (Virt Src) -> es:edi (Phys Dst)
775 */
776 STAM_STATS({ *ppStat = &pVM->iom.s.StatRZInstMovsToMMIO; });
777
778 /* Check callback. */
779 if (!pRange->CTX_SUFF(pfnWriteCallback))
780 return VINF_IOM_R3_MMIO_WRITE;
781
782 /* Convert source address ds:esi. */
783 RTGCUINTPTR pu8Virt;
784 rc = SELMToFlatEx(pVM, DISSELREG_DS, pRegFrame, (RTGCPTR)pRegFrame->rsi,
785 SELMTOFLAT_FLAGS_HYPER | SELMTOFLAT_FLAGS_NO_PL,
786 (PRTGCPTR)&pu8Virt);
787 if (RT_SUCCESS(rc))
788 {
789
790 /* Access verification first; we currently can't recover properly from traps inside this instruction */
791 rc = PGMVerifyAccess(pVCpu, pu8Virt, cTransfers * cb, (cpl == 3) ? X86_PTE_US : 0);
792 if (rc != VINF_SUCCESS)
793 {
794 Log(("MOVS will generate a trap -> recompiler, rc=%d\n", rc));
795 return VINF_EM_RAW_EMULATE_INSTR;
796 }
797
798#ifdef IN_RC
799 MMGCRamRegisterTrapHandler(pVM);
800#endif
801
802 /* copy loop. */
803 while (cTransfers)
804 {
805 uint32_t u32Data = 0;
806 rc = iomRamRead(pVCpu, &u32Data, (RTGCPTR)pu8Virt, cb);
807 if (rc != VINF_SUCCESS)
808 break;
809 rc = iomMMIODoWrite(pVM, pRange, Phys, &u32Data, cb);
810 if (rc != VINF_SUCCESS)
811 break;
812
813 pu8Virt += offIncrement;
814 Phys += offIncrement;
815 pRegFrame->rsi += offIncrement;
816 pRegFrame->rdi += offIncrement;
817 cTransfers--;
818 }
819#ifdef IN_RC
820 MMGCRamDeregisterTrapHandler(pVM);
821#endif
822 /* Update ecx. */
823 if (pCpu->fPrefix & DISPREFIX_REP)
824 pRegFrame->ecx = cTransfers;
825 }
826 else
827 rc = VINF_IOM_R3_MMIO_READ_WRITE;
828 }
829 else
830 {
831 /*
832 * Read operation: [MMIO] -> [mem] or [MMIO] -> [MMIO]
833 * ds:[eSI] (Phys Src) -> es:[eDI] (Virt Dst)
834 */
835 STAM_STATS({ *ppStat = &pVM->iom.s.StatRZInstMovsFromMMIO; });
836
837 /* Check callback. */
838 if (!pRange->CTX_SUFF(pfnReadCallback))
839 return VINF_IOM_R3_MMIO_READ;
840
841 /* Convert destination address. */
842 RTGCUINTPTR pu8Virt;
843 rc = SELMToFlatEx(pVM, DISSELREG_ES, pRegFrame, (RTGCPTR)pRegFrame->rdi,
844 SELMTOFLAT_FLAGS_HYPER | SELMTOFLAT_FLAGS_NO_PL,
845 (RTGCPTR *)&pu8Virt);
846 if (RT_FAILURE(rc))
847 return VINF_IOM_R3_MMIO_READ;
848
849 /* Check if destination address is MMIO. */
850 PIOMMMIORANGE pMMIODst;
851 RTGCPHYS PhysDst;
852 rc = PGMGstGetPage(pVCpu, (RTGCPTR)pu8Virt, NULL, &PhysDst);
853 PhysDst |= (RTGCUINTPTR)pu8Virt & PAGE_OFFSET_MASK;
854 if ( RT_SUCCESS(rc)
855 && (pMMIODst = iomMmioGetRangeWithRef(pVM, PhysDst)))
856 {
857 /** @todo implement per-device locks for MMIO access. */
858 Assert(!pMMIODst->CTX_SUFF(pDevIns)->CTX_SUFF(pCritSect));
859
860 /*
861 * Extra: [MMIO] -> [MMIO]
862 */
863 STAM_STATS({ *ppStat = &pVM->iom.s.StatRZInstMovsMMIO; });
864 if (!pMMIODst->CTX_SUFF(pfnWriteCallback) && pMMIODst->pfnWriteCallbackR3)
865 {
866 iomMmioReleaseRange(pVM, pRange);
867 return VINF_IOM_R3_MMIO_READ_WRITE;
868 }
869
870 /* copy loop. */
871 while (cTransfers)
872 {
873 uint32_t u32Data;
874 rc = iomMMIODoRead(pVM, pRange, Phys, &u32Data, cb);
875 if (rc != VINF_SUCCESS)
876 break;
877 rc = iomMMIODoWrite(pVM, pMMIODst, PhysDst, &u32Data, cb);
878 if (rc != VINF_SUCCESS)
879 break;
880
881 Phys += offIncrement;
882 PhysDst += offIncrement;
883 pRegFrame->rsi += offIncrement;
884 pRegFrame->rdi += offIncrement;
885 cTransfers--;
886 }
887 iomMmioReleaseRange(pVM, pRange);
888 }
889 else
890 {
891 /*
892 * Normal: [MMIO] -> [Mem]
893 */
894 /* Access verification first; we currently can't recover properly from traps inside this instruction */
895 rc = PGMVerifyAccess(pVCpu, pu8Virt, cTransfers * cb, X86_PTE_RW | ((cpl == 3) ? X86_PTE_US : 0));
896 if (rc != VINF_SUCCESS)
897 {
898 Log(("MOVS will generate a trap -> recompiler, rc=%d\n", rc));
899 return VINF_EM_RAW_EMULATE_INSTR;
900 }
901
902 /* copy loop. */
903#ifdef IN_RC
904 MMGCRamRegisterTrapHandler(pVM);
905#endif
906 while (cTransfers)
907 {
908 uint32_t u32Data;
909 rc = iomMMIODoRead(pVM, pRange, Phys, &u32Data, cb);
910 if (rc != VINF_SUCCESS)
911 break;
912 rc = iomRamWrite(pVCpu, pRegFrame, (RTGCPTR)pu8Virt, &u32Data, cb);
913 if (rc != VINF_SUCCESS)
914 {
915 Log(("iomRamWrite %08X size=%d failed with %d\n", pu8Virt, cb, rc));
916 break;
917 }
918
919 pu8Virt += offIncrement;
920 Phys += offIncrement;
921 pRegFrame->rsi += offIncrement;
922 pRegFrame->rdi += offIncrement;
923 cTransfers--;
924 }
925#ifdef IN_RC
926 MMGCRamDeregisterTrapHandler(pVM);
927#endif
928 }
929
930 /* Update ecx on exit. */
931 if (pCpu->fPrefix & DISPREFIX_REP)
932 pRegFrame->ecx = cTransfers;
933 }
934
935 /* work statistics. */
936 if (rc == VINF_SUCCESS)
937 iomMMIOStatLength(pVM, cb);
938 NOREF(ppStat);
939 return rc;
940}
941#endif /* IOM_WITH_MOVS_SUPPORT */
942
943
944/**
945 * Gets the address / opcode mask corresponding to the given CPU mode.
946 *
947 * @returns Mask.
948 * @param enmCpuMode CPU mode.
949 */
950static uint64_t iomDisModeToMask(DISCPUMODE enmCpuMode)
951{
952 switch (enmCpuMode)
953 {
954 case DISCPUMODE_16BIT: return UINT16_MAX;
955 case DISCPUMODE_32BIT: return UINT32_MAX;
956 case DISCPUMODE_64BIT: return UINT64_MAX;
957 default:
958 AssertFailedReturn(UINT32_MAX);
959 }
960}
961
962
963/**
964 * [REP] STOSB
965 * [REP] STOSW
966 * [REP] STOSD
967 *
968 * Restricted implementation.
969 *
970 *
971 * @returns VBox status code.
972 *
973 * @param pVM The virtual machine.
974 * @param pVCpu Pointer to the virtual CPU structure of the caller.
975 * @param pRegFrame Trap register frame.
976 * @param GCPhysFault The GC physical address corresponding to pvFault.
977 * @param pCpu Disassembler CPU state.
978 * @param pRange Pointer MMIO range.
979 */
980static int iomInterpretSTOS(PVM pVM, PVMCPU pVCpu, PCPUMCTXCORE pRegFrame, RTGCPHYS GCPhysFault,
981 PDISCPUSTATE pCpu, PIOMMMIORANGE pRange)
982{
983 /*
984 * We do not support segment prefixes or REPNE..
985 */
986 if (pCpu->fPrefix & (DISPREFIX_SEG | DISPREFIX_REPNE))
987 return VINF_IOM_R3_MMIO_READ_WRITE; /** @todo -> REM instead of HC */
988
989 /*
990 * Get bytes/words/dwords/qwords count to copy.
991 */
992 uint64_t const fAddrMask = iomDisModeToMask((DISCPUMODE)pCpu->uAddrMode);
993 RTGCUINTREG cTransfers = 1;
994 if (pCpu->fPrefix & DISPREFIX_REP)
995 {
996#ifndef IN_RC
997 if ( CPUMIsGuestIn64BitCode(pVCpu)
998 && pRegFrame->rcx >= _4G)
999 return VINF_EM_RAW_EMULATE_INSTR;
1000#endif
1001
1002 cTransfers = pRegFrame->rcx & fAddrMask;
1003 if (!cTransfers)
1004 return VINF_SUCCESS;
1005 }
1006
1007/** @todo r=bird: bounds checks! */
1008
1009 /*
1010 * Get data size.
1011 */
1012 unsigned cb = DISGetParamSize(pCpu, &pCpu->Param1);
1013 AssertMsg(cb > 0 && cb <= sizeof(uint64_t), ("cb=%d\n", cb));
1014 int offIncrement = pRegFrame->eflags.Bits.u1DF ? -(signed)cb : (signed)cb;
1015
1016#ifdef VBOX_WITH_STATISTICS
1017 if (pVM->iom.s.cStosMaxBytes < (cTransfers << SIZE_2_SHIFT(cb)))
1018 pVM->iom.s.cStosMaxBytes = cTransfers << SIZE_2_SHIFT(cb);
1019#endif
1020
1021
1022 RTGCPHYS Phys = GCPhysFault;
1023 int rc;
1024 if ( pRange->CTX_SUFF(pfnFillCallback)
1025 && cb <= 4 /* can only fill 32-bit values */)
1026 {
1027 /*
1028 * Use the fill callback.
1029 */
1030 /** @todo pfnFillCallback must return number of bytes successfully written!!! */
1031 if (offIncrement > 0)
1032 {
1033 /* addr++ variant. */
1034 rc = pRange->CTX_SUFF(pfnFillCallback)(pRange->CTX_SUFF(pDevIns), pRange->CTX_SUFF(pvUser), Phys,
1035 pRegFrame->eax, cb, cTransfers);
1036 if (rc == VINF_SUCCESS)
1037 {
1038 /* Update registers. */
1039 pRegFrame->rdi = ((pRegFrame->rdi + (cTransfers << SIZE_2_SHIFT(cb))) & fAddrMask)
1040 | (pRegFrame->rdi & ~fAddrMask);
1041 if (pCpu->fPrefix & DISPREFIX_REP)
1042 pRegFrame->rcx &= ~fAddrMask;
1043 }
1044 }
1045 else
1046 {
1047 /* addr-- variant. */
1048 rc = pRange->CTX_SUFF(pfnFillCallback)(pRange->CTX_SUFF(pDevIns), pRange->CTX_SUFF(pvUser),
1049 Phys - ((cTransfers - 1) << SIZE_2_SHIFT(cb)),
1050 pRegFrame->eax, cb, cTransfers);
1051 if (rc == VINF_SUCCESS)
1052 {
1053 /* Update registers. */
1054 pRegFrame->rdi = ((pRegFrame->rdi - (cTransfers << SIZE_2_SHIFT(cb))) & fAddrMask)
1055 | (pRegFrame->rdi & ~fAddrMask);
1056 if (pCpu->fPrefix & DISPREFIX_REP)
1057 pRegFrame->rcx &= ~fAddrMask;
1058 }
1059 }
1060 }
1061 else
1062 {
1063 /*
1064 * Use the write callback.
1065 */
1066 Assert(pRange->CTX_SUFF(pfnWriteCallback) || !pRange->pfnWriteCallbackR3);
1067 uint64_t u64Data = pRegFrame->rax;
1068
1069 /* fill loop. */
1070 do
1071 {
1072 rc = iomMMIODoWrite(pVM, pVCpu, pRange, Phys, &u64Data, cb);
1073 if (rc != VINF_SUCCESS)
1074 break;
1075
1076 Phys += offIncrement;
1077 pRegFrame->rdi = ((pRegFrame->rdi + offIncrement) & fAddrMask)
1078 | (pRegFrame->rdi & ~fAddrMask);
1079 cTransfers--;
1080 } while (cTransfers);
1081
1082 /* Update rcx on exit. */
1083 if (pCpu->fPrefix & DISPREFIX_REP)
1084 pRegFrame->rcx = (cTransfers & fAddrMask)
1085 | (pRegFrame->rcx & ~fAddrMask);
1086 }
1087
1088 /*
1089 * Work statistics and return.
1090 */
1091 if (rc == VINF_SUCCESS)
1092 iomMMIOStatLength(pVM, cb);
1093 return rc;
1094}
1095
1096
1097/**
1098 * [REP] LODSB
1099 * [REP] LODSW
1100 * [REP] LODSD
1101 *
1102 * Restricted implementation.
1103 *
1104 *
1105 * @returns VBox status code.
1106 *
1107 * @param pVM The virtual machine.
1108 * @param pVCpu Pointer to the virtual CPU structure of the caller.
1109 * @param pRegFrame Trap register frame.
1110 * @param GCPhysFault The GC physical address corresponding to pvFault.
1111 * @param pCpu Disassembler CPU state.
1112 * @param pRange Pointer MMIO range.
1113 */
1114static int iomInterpretLODS(PVM pVM, PVMCPU pVCpu, PCPUMCTXCORE pRegFrame, RTGCPHYS GCPhysFault, PDISCPUSTATE pCpu,
1115 PIOMMMIORANGE pRange)
1116{
1117 Assert(pRange->CTX_SUFF(pfnReadCallback) || !pRange->pfnReadCallbackR3);
1118
1119 /*
1120 * We do not support segment prefixes or REP*.
1121 */
1122 if (pCpu->fPrefix & (DISPREFIX_SEG | DISPREFIX_REP | DISPREFIX_REPNE))
1123 return VINF_IOM_R3_MMIO_READ_WRITE; /** @todo -> REM instead of HC */
1124
1125 /*
1126 * Get data size.
1127 */
1128 unsigned cb = DISGetParamSize(pCpu, &pCpu->Param2);
1129 AssertMsg(cb > 0 && cb <= sizeof(uint64_t), ("cb=%d\n", cb));
1130 int offIncrement = pRegFrame->eflags.Bits.u1DF ? -(signed)cb : (signed)cb;
1131
1132 /*
1133 * Perform read.
1134 */
1135 int rc = iomMMIODoRead(pVM, pVCpu, pRange, GCPhysFault, &pRegFrame->rax, cb);
1136 if (rc == VINF_SUCCESS)
1137 {
1138 uint64_t const fAddrMask = iomDisModeToMask((DISCPUMODE)pCpu->uAddrMode);
1139 pRegFrame->rsi = ((pRegFrame->rsi + offIncrement) & fAddrMask)
1140 | (pRegFrame->rsi & ~fAddrMask);
1141 }
1142
1143 /*
1144 * Work statistics and return.
1145 */
1146 if (rc == VINF_SUCCESS)
1147 iomMMIOStatLength(pVM, cb);
1148 return rc;
1149}
1150
1151
1152/**
1153 * CMP [MMIO], reg|imm
1154 * CMP reg|imm, [MMIO]
1155 *
1156 * Restricted implementation.
1157 *
1158 *
1159 * @returns VBox status code.
1160 *
1161 * @param pVM The virtual machine.
1162 * @param pRegFrame Trap register frame.
1163 * @param GCPhysFault The GC physical address corresponding to pvFault.
1164 * @param pCpu Disassembler CPU state.
1165 * @param pRange Pointer MMIO range.
1166 */
1167static int iomInterpretCMP(PVM pVM, PVMCPU pVCpu, PCPUMCTXCORE pRegFrame, RTGCPHYS GCPhysFault, PDISCPUSTATE pCpu,
1168 PIOMMMIORANGE pRange)
1169{
1170 Assert(pRange->CTX_SUFF(pfnReadCallback) || !pRange->pfnReadCallbackR3);
1171
1172 /*
1173 * Get the operands.
1174 */
1175 unsigned cb = 0;
1176 uint64_t uData1 = 0;
1177 uint64_t uData2 = 0;
1178 int rc;
1179 if (iomGetRegImmData(pCpu, &pCpu->Param1, pRegFrame, &uData1, &cb))
1180 /* cmp reg, [MMIO]. */
1181 rc = iomMMIODoRead(pVM, pVCpu, pRange, GCPhysFault, &uData2, cb);
1182 else if (iomGetRegImmData(pCpu, &pCpu->Param2, pRegFrame, &uData2, &cb))
1183 /* cmp [MMIO], reg|imm. */
1184 rc = iomMMIODoRead(pVM, pVCpu, pRange, GCPhysFault, &uData1, cb);
1185 else
1186 {
1187 AssertMsgFailed(("Disassember CMP problem..\n"));
1188 rc = VERR_IOM_MMIO_HANDLER_DISASM_ERROR;
1189 }
1190
1191 if (rc == VINF_SUCCESS)
1192 {
1193#if HC_ARCH_BITS == 32
1194 /* Can't deal with 8 byte operands in our 32-bit emulation code. */
1195 if (cb > 4)
1196 return VINF_IOM_R3_MMIO_READ_WRITE;
1197#endif
1198 /* Emulate CMP and update guest flags. */
1199 uint32_t eflags = EMEmulateCmp(uData1, uData2, cb);
1200 pRegFrame->eflags.u32 = (pRegFrame->eflags.u32 & ~(X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF | X86_EFL_OF))
1201 | (eflags & (X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF | X86_EFL_OF));
1202 iomMMIOStatLength(pVM, cb);
1203 }
1204
1205 return rc;
1206}
1207
1208
1209/**
1210 * AND [MMIO], reg|imm
1211 * AND reg, [MMIO]
1212 * OR [MMIO], reg|imm
1213 * OR reg, [MMIO]
1214 *
1215 * Restricted implementation.
1216 *
1217 *
1218 * @returns VBox status code.
1219 *
1220 * @param pVM The virtual machine.
1221 * @param pVCpu Pointer to the virtual CPU structure of the caller.
1222 * @param pRegFrame Trap register frame.
1223 * @param GCPhysFault The GC physical address corresponding to pvFault.
1224 * @param pCpu Disassembler CPU state.
1225 * @param pRange Pointer MMIO range.
1226 * @param pfnEmulate Instruction emulation function.
1227 */
1228static int iomInterpretOrXorAnd(PVM pVM, PVMCPU pVCpu, PCPUMCTXCORE pRegFrame, RTGCPHYS GCPhysFault, PDISCPUSTATE pCpu,
1229 PIOMMMIORANGE pRange, PFNEMULATEPARAM3 pfnEmulate)
1230{
1231 unsigned cb = 0;
1232 uint64_t uData1 = 0;
1233 uint64_t uData2 = 0;
1234 bool fAndWrite;
1235 int rc;
1236
1237#ifdef LOG_ENABLED
1238 const char *pszInstr;
1239
1240 if (pCpu->pCurInstr->uOpcode == OP_XOR)
1241 pszInstr = "Xor";
1242 else if (pCpu->pCurInstr->uOpcode == OP_OR)
1243 pszInstr = "Or";
1244 else if (pCpu->pCurInstr->uOpcode == OP_AND)
1245 pszInstr = "And";
1246 else
1247 pszInstr = "OrXorAnd??";
1248#endif
1249
1250 if (iomGetRegImmData(pCpu, &pCpu->Param1, pRegFrame, &uData1, &cb))
1251 {
1252#if HC_ARCH_BITS == 32
1253 /* Can't deal with 8 byte operands in our 32-bit emulation code. */
1254 if (cb > 4)
1255 return VINF_IOM_R3_MMIO_READ_WRITE;
1256#endif
1257 /* and reg, [MMIO]. */
1258 Assert(pRange->CTX_SUFF(pfnReadCallback) || !pRange->pfnReadCallbackR3);
1259 fAndWrite = false;
1260 rc = iomMMIODoRead(pVM, pVCpu, pRange, GCPhysFault, &uData2, cb);
1261 }
1262 else if (iomGetRegImmData(pCpu, &pCpu->Param2, pRegFrame, &uData2, &cb))
1263 {
1264#if HC_ARCH_BITS == 32
1265 /* Can't deal with 8 byte operands in our 32-bit emulation code. */
1266 if (cb > 4)
1267 return VINF_IOM_R3_MMIO_READ_WRITE;
1268#endif
1269 /* and [MMIO], reg|imm. */
1270 fAndWrite = true;
1271 if ( (pRange->CTX_SUFF(pfnReadCallback) || !pRange->pfnReadCallbackR3)
1272 && (pRange->CTX_SUFF(pfnWriteCallback) || !pRange->pfnWriteCallbackR3))
1273 rc = iomMMIODoRead(pVM, pVCpu, pRange, GCPhysFault, &uData1, cb);
1274 else
1275 rc = VINF_IOM_R3_MMIO_READ_WRITE;
1276 }
1277 else
1278 {
1279 AssertMsgFailed(("Disassember AND problem..\n"));
1280 return VERR_IOM_MMIO_HANDLER_DISASM_ERROR;
1281 }
1282
1283 if (rc == VINF_SUCCESS)
1284 {
1285 /* Emulate AND and update guest flags. */
1286 uint32_t eflags = pfnEmulate((uint32_t *)&uData1, uData2, cb);
1287
1288 LogFlow(("iomInterpretOrXorAnd %s result %RX64\n", pszInstr, uData1));
1289
1290 if (fAndWrite)
1291 /* Store result to MMIO. */
1292 rc = iomMMIODoWrite(pVM, pVCpu, pRange, GCPhysFault, &uData1, cb);
1293 else
1294 {
1295 /* Store result to register. */
1296 bool fRc = iomSaveDataToReg(pCpu, &pCpu->Param1, pRegFrame, uData1);
1297 AssertMsg(fRc, ("Failed to store register value!\n")); NOREF(fRc);
1298 }
1299 if (rc == VINF_SUCCESS)
1300 {
1301 /* Update guest's eflags and finish. */
1302 pRegFrame->eflags.u32 = (pRegFrame->eflags.u32 & ~(X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF | X86_EFL_OF))
1303 | (eflags & (X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF | X86_EFL_OF));
1304 iomMMIOStatLength(pVM, cb);
1305 }
1306 }
1307
1308 return rc;
1309}
1310
1311
1312/**
1313 * TEST [MMIO], reg|imm
1314 * TEST reg, [MMIO]
1315 *
1316 * Restricted implementation.
1317 *
1318 *
1319 * @returns VBox status code.
1320 *
1321 * @param pVM The virtual machine.
1322 * @param pVCpu Pointer to the virtual CPU structure of the caller.
1323 * @param pRegFrame Trap register frame.
1324 * @param GCPhysFault The GC physical address corresponding to pvFault.
1325 * @param pCpu Disassembler CPU state.
1326 * @param pRange Pointer MMIO range.
1327 */
1328static int iomInterpretTEST(PVM pVM, PVMCPU pVCpu, PCPUMCTXCORE pRegFrame, RTGCPHYS GCPhysFault, PDISCPUSTATE pCpu,
1329 PIOMMMIORANGE pRange)
1330{
1331 Assert(pRange->CTX_SUFF(pfnReadCallback) || !pRange->pfnReadCallbackR3);
1332
1333 unsigned cb = 0;
1334 uint64_t uData1 = 0;
1335 uint64_t uData2 = 0;
1336 int rc;
1337
1338 if (iomGetRegImmData(pCpu, &pCpu->Param1, pRegFrame, &uData1, &cb))
1339 {
1340 /* and test, [MMIO]. */
1341 rc = iomMMIODoRead(pVM, pVCpu, pRange, GCPhysFault, &uData2, cb);
1342 }
1343 else if (iomGetRegImmData(pCpu, &pCpu->Param2, pRegFrame, &uData2, &cb))
1344 {
1345 /* test [MMIO], reg|imm. */
1346 rc = iomMMIODoRead(pVM, pVCpu, pRange, GCPhysFault, &uData1, cb);
1347 }
1348 else
1349 {
1350 AssertMsgFailed(("Disassember TEST problem..\n"));
1351 return VERR_IOM_MMIO_HANDLER_DISASM_ERROR;
1352 }
1353
1354 if (rc == VINF_SUCCESS)
1355 {
1356#if HC_ARCH_BITS == 32
1357 /* Can't deal with 8 byte operands in our 32-bit emulation code. */
1358 if (cb > 4)
1359 return VINF_IOM_R3_MMIO_READ_WRITE;
1360#endif
1361
1362 /* Emulate TEST (=AND without write back) and update guest EFLAGS. */
1363 uint32_t eflags = EMEmulateAnd((uint32_t *)&uData1, uData2, cb);
1364 pRegFrame->eflags.u32 = (pRegFrame->eflags.u32 & ~(X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF | X86_EFL_OF))
1365 | (eflags & (X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF | X86_EFL_OF));
1366 iomMMIOStatLength(pVM, cb);
1367 }
1368
1369 return rc;
1370}
1371
1372
1373/**
1374 * BT [MMIO], reg|imm
1375 *
1376 * Restricted implementation.
1377 *
1378 *
1379 * @returns VBox status code.
1380 *
1381 * @param pVM The virtual machine.
1382 * @param pVCpu Pointer to the virtual CPU structure of the caller.
1383 * @param pRegFrame Trap register frame.
1384 * @param GCPhysFault The GC physical address corresponding to pvFault.
1385 * @param pCpu Disassembler CPU state.
1386 * @param pRange Pointer MMIO range.
1387 */
1388static int iomInterpretBT(PVM pVM, PVMCPU pVCpu, PCPUMCTXCORE pRegFrame, RTGCPHYS GCPhysFault, PDISCPUSTATE pCpu,
1389 PIOMMMIORANGE pRange)
1390{
1391 Assert(pRange->CTX_SUFF(pfnReadCallback) || !pRange->pfnReadCallbackR3);
1392
1393 uint64_t uBit = 0;
1394 uint64_t uData = 0;
1395 unsigned cbIgnored;
1396
1397 if (!iomGetRegImmData(pCpu, &pCpu->Param2, pRegFrame, &uBit, &cbIgnored))
1398 {
1399 AssertMsgFailed(("Disassember BT problem..\n"));
1400 return VERR_IOM_MMIO_HANDLER_DISASM_ERROR;
1401 }
1402 /* The size of the memory operand only matters here. */
1403 unsigned cbData = DISGetParamSize(pCpu, &pCpu->Param1);
1404
1405 /* bt [MMIO], reg|imm. */
1406 int rc = iomMMIODoRead(pVM, pVCpu, pRange, GCPhysFault, &uData, cbData);
1407 if (rc == VINF_SUCCESS)
1408 {
1409 /* Find the bit inside the faulting address */
1410 pRegFrame->eflags.Bits.u1CF = (uData >> uBit);
1411 iomMMIOStatLength(pVM, cbData);
1412 }
1413
1414 return rc;
1415}
1416
1417/**
1418 * XCHG [MMIO], reg
1419 * XCHG reg, [MMIO]
1420 *
1421 * Restricted implementation.
1422 *
1423 *
1424 * @returns VBox status code.
1425 *
1426 * @param pVM The virtual machine.
1427 * @param pVCpu Pointer to the virtual CPU structure of the caller.
1428 * @param pRegFrame Trap register frame.
1429 * @param GCPhysFault The GC physical address corresponding to pvFault.
1430 * @param pCpu Disassembler CPU state.
1431 * @param pRange Pointer MMIO range.
1432 */
1433static int iomInterpretXCHG(PVM pVM, PVMCPU pVCpu, PCPUMCTXCORE pRegFrame, RTGCPHYS GCPhysFault, PDISCPUSTATE pCpu,
1434 PIOMMMIORANGE pRange)
1435{
1436 /* Check for read & write handlers since IOMMMIOHandler doesn't cover this. */
1437 if ( (!pRange->CTX_SUFF(pfnReadCallback) && pRange->pfnReadCallbackR3)
1438 || (!pRange->CTX_SUFF(pfnWriteCallback) && pRange->pfnWriteCallbackR3))
1439 return VINF_IOM_R3_MMIO_READ_WRITE;
1440
1441 int rc;
1442 unsigned cb = 0;
1443 uint64_t uData1 = 0;
1444 uint64_t uData2 = 0;
1445 if (iomGetRegImmData(pCpu, &pCpu->Param1, pRegFrame, &uData1, &cb))
1446 {
1447 /* xchg reg, [MMIO]. */
1448 rc = iomMMIODoRead(pVM, pVCpu, pRange, GCPhysFault, &uData2, cb);
1449 if (rc == VINF_SUCCESS)
1450 {
1451 /* Store result to MMIO. */
1452 rc = iomMMIODoWrite(pVM, pVCpu, pRange, GCPhysFault, &uData1, cb);
1453
1454 if (rc == VINF_SUCCESS)
1455 {
1456 /* Store result to register. */
1457 bool fRc = iomSaveDataToReg(pCpu, &pCpu->Param1, pRegFrame, uData2);
1458 AssertMsg(fRc, ("Failed to store register value!\n")); NOREF(fRc);
1459 }
1460 else
1461 Assert(rc == VINF_IOM_R3_MMIO_WRITE || rc == VINF_PATM_HC_MMIO_PATCH_WRITE);
1462 }
1463 else
1464 Assert(rc == VINF_IOM_R3_MMIO_READ || rc == VINF_PATM_HC_MMIO_PATCH_READ);
1465 }
1466 else if (iomGetRegImmData(pCpu, &pCpu->Param2, pRegFrame, &uData2, &cb))
1467 {
1468 /* xchg [MMIO], reg. */
1469 rc = iomMMIODoRead(pVM, pVCpu, pRange, GCPhysFault, &uData1, cb);
1470 if (rc == VINF_SUCCESS)
1471 {
1472 /* Store result to MMIO. */
1473 rc = iomMMIODoWrite(pVM, pVCpu, pRange, GCPhysFault, &uData2, cb);
1474 if (rc == VINF_SUCCESS)
1475 {
1476 /* Store result to register. */
1477 bool fRc = iomSaveDataToReg(pCpu, &pCpu->Param2, pRegFrame, uData1);
1478 AssertMsg(fRc, ("Failed to store register value!\n")); NOREF(fRc);
1479 }
1480 else
1481 AssertMsg(rc == VINF_IOM_R3_MMIO_READ_WRITE || rc == VINF_IOM_R3_MMIO_WRITE || rc == VINF_PATM_HC_MMIO_PATCH_WRITE || rc == VINF_EM_RAW_EMULATE_IO_BLOCK, ("rc=%Rrc\n", rc));
1482 }
1483 else
1484 AssertMsg(rc == VINF_IOM_R3_MMIO_READ_WRITE || rc == VINF_IOM_R3_MMIO_READ || rc == VINF_PATM_HC_MMIO_PATCH_READ || rc == VINF_EM_RAW_EMULATE_IO_BLOCK, ("rc=%Rrc\n", rc));
1485 }
1486 else
1487 {
1488 AssertMsgFailed(("Disassember XCHG problem..\n"));
1489 rc = VERR_IOM_MMIO_HANDLER_DISASM_ERROR;
1490 }
1491 return rc;
1492}
1493
1494
1495/**
1496 * \#PF Handler callback for MMIO ranges.
1497 *
1498 * @returns VBox status code (appropriate for GC return).
1499 * @param pVM Pointer to the VM.
1500 * @param pVCpu Pointer to the virtual CPU structure of the caller.
1501 * @param uErrorCode CPU Error code. This is UINT32_MAX when we don't have
1502 * any error code (the EPT misconfig hack).
1503 * @param pCtxCore Trap register frame.
1504 * @param GCPhysFault The GC physical address corresponding to pvFault.
1505 * @param pvUser Pointer to the MMIO ring-3 range entry.
1506 */
1507static int iomMMIOHandler(PVM pVM, PVMCPU pVCpu, uint32_t uErrorCode, PCPUMCTXCORE pCtxCore, RTGCPHYS GCPhysFault, void *pvUser)
1508{
1509 int rc = IOM_LOCK_SHARED(pVM);
1510#ifndef IN_RING3
1511 if (rc == VERR_SEM_BUSY)
1512 return VINF_IOM_R3_MMIO_READ_WRITE;
1513#endif
1514 AssertRC(rc);
1515
1516 STAM_PROFILE_START(&pVM->iom.s.StatRZMMIOHandler, a);
1517 Log(("iomMMIOHandler: GCPhys=%RGp uErr=%#x rip=%RGv\n", GCPhysFault, uErrorCode, (RTGCPTR)pCtxCore->rip));
1518
1519 PIOMMMIORANGE pRange = (PIOMMMIORANGE)pvUser;
1520 Assert(pRange);
1521 Assert(pRange == iomMmioGetRange(pVM, pVCpu, GCPhysFault));
1522 iomMmioRetainRange(pRange);
1523#ifndef VBOX_WITH_STATISTICS
1524 IOM_UNLOCK_SHARED(pVM);
1525
1526#else
1527 /*
1528 * Locate the statistics.
1529 */
1530 PIOMMMIOSTATS pStats = iomMmioGetStats(pVM, pVCpu, GCPhysFault, pRange);
1531 if (!pStats)
1532 {
1533 iomMmioReleaseRange(pVM, pRange);
1534# ifdef IN_RING3
1535 return VERR_NO_MEMORY;
1536# else
1537 STAM_PROFILE_STOP(&pVM->iom.s.StatRZMMIOHandler, a);
1538 STAM_COUNTER_INC(&pVM->iom.s.StatRZMMIOFailures);
1539 return VINF_IOM_R3_MMIO_READ_WRITE;
1540# endif
1541 }
1542#endif
1543
1544#ifndef IN_RING3
1545 /*
1546 * Should we defer the request right away? This isn't usually the case, so
1547 * do the simple test first and the try deal with uErrorCode being N/A.
1548 */
1549 if (RT_UNLIKELY( ( !pRange->CTX_SUFF(pfnWriteCallback)
1550 || !pRange->CTX_SUFF(pfnReadCallback))
1551 && ( uErrorCode == UINT32_MAX
1552 ? pRange->pfnWriteCallbackR3 || pRange->pfnReadCallbackR3
1553 : uErrorCode & X86_TRAP_PF_RW
1554 ? !pRange->CTX_SUFF(pfnWriteCallback) && pRange->pfnWriteCallbackR3
1555 : !pRange->CTX_SUFF(pfnReadCallback) && pRange->pfnReadCallbackR3
1556 )
1557 )
1558 )
1559 {
1560 if (uErrorCode & X86_TRAP_PF_RW)
1561 STAM_COUNTER_INC(&pStats->CTX_MID_Z(Write,ToR3));
1562 else
1563 STAM_COUNTER_INC(&pStats->CTX_MID_Z(Read,ToR3));
1564
1565 STAM_PROFILE_STOP(&pVM->iom.s.StatRZMMIOHandler, a);
1566 STAM_COUNTER_INC(&pVM->iom.s.StatRZMMIOFailures);
1567 iomMmioReleaseRange(pVM, pRange);
1568 return VINF_IOM_R3_MMIO_READ_WRITE;
1569 }
1570#endif /* !IN_RING3 */
1571
1572 /*
1573 * Retain the range and do locking.
1574 */
1575 PPDMDEVINS pDevIns = pRange->CTX_SUFF(pDevIns);
1576 rc = PDMCritSectEnter(pDevIns->CTX_SUFF(pCritSectRo), VINF_IOM_R3_MMIO_READ_WRITE);
1577 if (rc != VINF_SUCCESS)
1578 {
1579 iomMmioReleaseRange(pVM, pRange);
1580 return rc;
1581 }
1582
1583 /*
1584 * Disassemble the instruction and interpret it.
1585 */
1586 PDISCPUSTATE pDis = &pVCpu->iom.s.DisState;
1587 unsigned cbOp;
1588 rc = EMInterpretDisasCurrent(pVM, pVCpu, pDis, &cbOp);
1589 if (RT_FAILURE(rc))
1590 {
1591 PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo));
1592 iomMmioReleaseRange(pVM, pRange);
1593 return rc;
1594 }
1595 switch (pDis->pCurInstr->uOpcode)
1596 {
1597 case OP_MOV:
1598 case OP_MOVZX:
1599 case OP_MOVSX:
1600 {
1601 STAM_PROFILE_START(&pVM->iom.s.StatRZInstMov, b);
1602 AssertMsg(uErrorCode == UINT32_MAX || DISUSE_IS_EFFECTIVE_ADDR(pDis->Param1.fUse) == !!(uErrorCode & X86_TRAP_PF_RW), ("flags1=%#llx/%RTbool flags2=%#llx/%RTbool ErrCd=%#x\n", pDis->Param1.fUse, DISUSE_IS_EFFECTIVE_ADDR(pDis->Param1.fUse), pDis->Param2.fUse, DISUSE_IS_EFFECTIVE_ADDR(pDis->Param2.fUse), uErrorCode));
1603 if (uErrorCode != UINT32_MAX /* EPT+MMIO optimization */
1604 ? uErrorCode & X86_TRAP_PF_RW
1605 : DISUSE_IS_EFFECTIVE_ADDR(pDis->Param1.fUse))
1606 rc = iomInterpretMOVxXWrite(pVM, pVCpu, pCtxCore, pDis, pRange, GCPhysFault);
1607 else
1608 rc = iomInterpretMOVxXRead(pVM, pVCpu, pCtxCore, pDis, pRange, GCPhysFault);
1609 STAM_PROFILE_STOP(&pVM->iom.s.StatRZInstMov, b);
1610 break;
1611 }
1612
1613
1614#ifdef IOM_WITH_MOVS_SUPPORT
1615 case OP_MOVSB:
1616 case OP_MOVSWD:
1617 {
1618 if (uErrorCode == UINT32_MAX)
1619 rc = VINF_IOM_R3_MMIO_READ_WRITE;
1620 else
1621 {
1622 STAM_PROFILE_ADV_START(&pVM->iom.s.StatRZInstMovs, c);
1623 PSTAMPROFILE pStat = NULL;
1624 rc = iomInterpretMOVS(pVM, !!(uErrorCode & X86_TRAP_PF_RW), pCtxCore, GCPhysFault, pDis, pRange, &pStat);
1625 STAM_PROFILE_ADV_STOP_EX(&pVM->iom.s.StatRZInstMovs, pStat, c);
1626 }
1627 break;
1628 }
1629#endif
1630
1631 case OP_STOSB:
1632 case OP_STOSWD:
1633 Assert(uErrorCode & X86_TRAP_PF_RW);
1634 STAM_PROFILE_START(&pVM->iom.s.StatRZInstStos, d);
1635 rc = iomInterpretSTOS(pVM, pVCpu, pCtxCore, GCPhysFault, pDis, pRange);
1636 STAM_PROFILE_STOP(&pVM->iom.s.StatRZInstStos, d);
1637 break;
1638
1639 case OP_LODSB:
1640 case OP_LODSWD:
1641 Assert(!(uErrorCode & X86_TRAP_PF_RW) || uErrorCode == UINT32_MAX);
1642 STAM_PROFILE_START(&pVM->iom.s.StatRZInstLods, e);
1643 rc = iomInterpretLODS(pVM, pVCpu, pCtxCore, GCPhysFault, pDis, pRange);
1644 STAM_PROFILE_STOP(&pVM->iom.s.StatRZInstLods, e);
1645 break;
1646
1647 case OP_CMP:
1648 Assert(!(uErrorCode & X86_TRAP_PF_RW) || uErrorCode == UINT32_MAX);
1649 STAM_PROFILE_START(&pVM->iom.s.StatRZInstCmp, f);
1650 rc = iomInterpretCMP(pVM, pVCpu, pCtxCore, GCPhysFault, pDis, pRange);
1651 STAM_PROFILE_STOP(&pVM->iom.s.StatRZInstCmp, f);
1652 break;
1653
1654 case OP_AND:
1655 STAM_PROFILE_START(&pVM->iom.s.StatRZInstAnd, g);
1656 rc = iomInterpretOrXorAnd(pVM, pVCpu, pCtxCore, GCPhysFault, pDis, pRange, EMEmulateAnd);
1657 STAM_PROFILE_STOP(&pVM->iom.s.StatRZInstAnd, g);
1658 break;
1659
1660 case OP_OR:
1661 STAM_PROFILE_START(&pVM->iom.s.StatRZInstOr, k);
1662 rc = iomInterpretOrXorAnd(pVM, pVCpu, pCtxCore, GCPhysFault, pDis, pRange, EMEmulateOr);
1663 STAM_PROFILE_STOP(&pVM->iom.s.StatRZInstOr, k);
1664 break;
1665
1666 case OP_XOR:
1667 STAM_PROFILE_START(&pVM->iom.s.StatRZInstXor, m);
1668 rc = iomInterpretOrXorAnd(pVM, pVCpu, pCtxCore, GCPhysFault, pDis, pRange, EMEmulateXor);
1669 STAM_PROFILE_STOP(&pVM->iom.s.StatRZInstXor, m);
1670 break;
1671
1672 case OP_TEST:
1673 Assert(!(uErrorCode & X86_TRAP_PF_RW) || uErrorCode == UINT32_MAX);
1674 STAM_PROFILE_START(&pVM->iom.s.StatRZInstTest, h);
1675 rc = iomInterpretTEST(pVM, pVCpu, pCtxCore, GCPhysFault, pDis, pRange);
1676 STAM_PROFILE_STOP(&pVM->iom.s.StatRZInstTest, h);
1677 break;
1678
1679 case OP_BT:
1680 Assert(!(uErrorCode & X86_TRAP_PF_RW) || uErrorCode == UINT32_MAX);
1681 STAM_PROFILE_START(&pVM->iom.s.StatRZInstBt, l);
1682 rc = iomInterpretBT(pVM, pVCpu, pCtxCore, GCPhysFault, pDis, pRange);
1683 STAM_PROFILE_STOP(&pVM->iom.s.StatRZInstBt, l);
1684 break;
1685
1686 case OP_XCHG:
1687 STAM_PROFILE_START(&pVM->iom.s.StatRZInstXchg, i);
1688 rc = iomInterpretXCHG(pVM, pVCpu, pCtxCore, GCPhysFault, pDis, pRange);
1689 STAM_PROFILE_STOP(&pVM->iom.s.StatRZInstXchg, i);
1690 break;
1691
1692
1693 /*
1694 * The instruction isn't supported. Hand it on to ring-3.
1695 */
1696 default:
1697 STAM_COUNTER_INC(&pVM->iom.s.StatRZInstOther);
1698 rc = VINF_IOM_R3_MMIO_READ_WRITE;
1699 break;
1700 }
1701
1702 /*
1703 * On success advance EIP.
1704 */
1705 if (rc == VINF_SUCCESS)
1706 pCtxCore->rip += cbOp;
1707 else
1708 {
1709 STAM_COUNTER_INC(&pVM->iom.s.StatRZMMIOFailures);
1710#if defined(VBOX_WITH_STATISTICS) && !defined(IN_RING3)
1711 switch (rc)
1712 {
1713 case VINF_IOM_R3_MMIO_READ:
1714 case VINF_IOM_R3_MMIO_READ_WRITE:
1715 STAM_COUNTER_INC(&pStats->CTX_MID_Z(Read,ToR3));
1716 break;
1717 case VINF_IOM_R3_MMIO_WRITE:
1718 STAM_COUNTER_INC(&pStats->CTX_MID_Z(Write,ToR3));
1719 break;
1720 }
1721#endif
1722 }
1723
1724 STAM_PROFILE_STOP(&pVM->iom.s.StatRZMMIOHandler, a);
1725 PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo));
1726 iomMmioReleaseRange(pVM, pRange);
1727 return rc;
1728}
1729
1730/**
1731 * \#PF Handler callback for MMIO ranges.
1732 *
1733 * @returns VBox status code (appropriate for GC return).
1734 * @param pVM Pointer to the VM.
1735 * @param uErrorCode CPU Error code.
1736 * @param pCtxCore Trap register frame.
1737 * @param pvFault The fault address (cr2).
1738 * @param GCPhysFault The GC physical address corresponding to pvFault.
1739 * @param pvUser Pointer to the MMIO ring-3 range entry.
1740 */
1741VMMDECL(int) IOMMMIOHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pCtxCore, RTGCPTR pvFault, RTGCPHYS GCPhysFault, void *pvUser)
1742{
1743 LogFlow(("IOMMMIOHandler: GCPhys=%RGp uErr=%#x pvFault=%RGv rip=%RGv\n",
1744 GCPhysFault, (uint32_t)uErrorCode, pvFault, (RTGCPTR)pCtxCore->rip));
1745 VBOXSTRICTRC rcStrict = iomMMIOHandler(pVM, VMMGetCpu(pVM), (uint32_t)uErrorCode, pCtxCore, GCPhysFault, pvUser);
1746 return VBOXSTRICTRC_VAL(rcStrict);
1747}
1748
1749/**
1750 * Physical access handler for MMIO ranges.
1751 *
1752 * @returns VBox status code (appropriate for GC return).
1753 * @param pVM Pointer to the VM.
1754 * @param pVCpu Pointer to the virtual CPU structure of the caller.
1755 * @param uErrorCode CPU Error code.
1756 * @param pCtxCore Trap register frame.
1757 * @param GCPhysFault The GC physical address.
1758 */
1759VMMDECL(VBOXSTRICTRC) IOMMMIOPhysHandler(PVM pVM, PVMCPU pVCpu, RTGCUINT uErrorCode, PCPUMCTXCORE pCtxCore, RTGCPHYS GCPhysFault)
1760{
1761 /*
1762 * We don't have a range here, so look it up before calling the common function.
1763 */
1764 int rc2 = IOM_LOCK_SHARED(pVM); NOREF(rc2);
1765#ifndef IN_RING3
1766 if (rc2 == VERR_SEM_BUSY)
1767 return VINF_IOM_R3_MMIO_READ_WRITE;
1768#endif
1769 PIOMMMIORANGE pRange = iomMmioGetRange(pVM, pVCpu, GCPhysFault);
1770 if (RT_UNLIKELY(!pRange))
1771 {
1772 IOM_UNLOCK_SHARED(pVM);
1773 return VERR_IOM_MMIO_RANGE_NOT_FOUND;
1774 }
1775 iomMmioRetainRange(pRange);
1776 IOM_UNLOCK_SHARED(pVM);
1777
1778 VBOXSTRICTRC rcStrict = iomMMIOHandler(pVM, pVCpu, (uint32_t)uErrorCode, pCtxCore, GCPhysFault, pRange);
1779
1780 iomMmioReleaseRange(pVM, pRange);
1781 return VBOXSTRICTRC_VAL(rcStrict);
1782}
1783
1784
1785#ifdef IN_RING3
1786/**
1787 * \#PF Handler callback for MMIO ranges.
1788 *
1789 * @returns VINF_SUCCESS if the handler have carried out the operation.
1790 * @returns VINF_PGM_HANDLER_DO_DEFAULT if the caller should carry out the access operation.
1791 * @param pVM Pointer to the VM.
1792 * @param GCPhys The physical address the guest is writing to.
1793 * @param pvPhys The HC mapping of that address.
1794 * @param pvBuf What the guest is reading/writing.
1795 * @param cbBuf How much it's reading/writing.
1796 * @param enmAccessType The access type.
1797 * @param pvUser Pointer to the MMIO range entry.
1798 */
1799DECLCALLBACK(int) IOMR3MMIOHandler(PVM pVM, RTGCPHYS GCPhysFault, void *pvPhys, void *pvBuf, size_t cbBuf,
1800 PGMACCESSTYPE enmAccessType, void *pvUser)
1801{
1802 PVMCPU pVCpu = VMMGetCpu(pVM);
1803 PIOMMMIORANGE pRange = (PIOMMMIORANGE)pvUser;
1804 STAM_COUNTER_INC(&pVM->iom.s.StatR3MMIOHandler);
1805
1806 AssertMsg(cbBuf >= 1 && cbBuf <= 16, ("%zu\n", cbBuf));
1807 AssertPtr(pRange);
1808 NOREF(pvPhys);
1809
1810 /*
1811 * Validate the range.
1812 */
1813 int rc = IOM_LOCK_SHARED(pVM);
1814 AssertRC(rc);
1815 Assert(pRange == iomMmioGetRange(pVM, pVCpu, GCPhysFault));
1816
1817 /*
1818 * Perform locking.
1819 */
1820 iomMmioRetainRange(pRange);
1821 PPDMDEVINS pDevIns = pRange->CTX_SUFF(pDevIns);
1822 IOM_UNLOCK_SHARED(pVM);
1823 rc = PDMCritSectEnter(pDevIns->CTX_SUFF(pCritSectRo), VINF_IOM_R3_MMIO_READ_WRITE);
1824 if (rc != VINF_SUCCESS)
1825 {
1826 iomMmioReleaseRange(pVM, pRange);
1827 return rc;
1828 }
1829
1830 /*
1831 * Perform the access.
1832 */
1833 if (enmAccessType == PGMACCESSTYPE_READ)
1834 rc = iomMMIODoRead(pVM, pVCpu, pRange, GCPhysFault, pvBuf, (unsigned)cbBuf);
1835 else
1836 rc = iomMMIODoWrite(pVM, pVCpu, pRange, GCPhysFault, pvBuf, (unsigned)cbBuf);
1837
1838 AssertRC(rc);
1839 iomMmioReleaseRange(pVM, pRange);
1840 PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo));
1841 return rc;
1842}
1843#endif /* IN_RING3 */
1844
1845
1846/**
1847 * Reads a MMIO register.
1848 *
1849 * @returns VBox status code.
1850 *
1851 * @param pVM Pointer to the VM.
1852 * @param pVCpu Pointer to the virtual CPU structure of the caller.
1853 * @param GCPhys The physical address to read.
1854 * @param pu32Value Where to store the value read.
1855 * @param cbValue The size of the register to read in bytes. 1, 2 or 4 bytes.
1856 */
1857VMMDECL(VBOXSTRICTRC) IOMMMIORead(PVM pVM, PVMCPU pVCpu, RTGCPHYS GCPhys, uint32_t *pu32Value, size_t cbValue)
1858{
1859 /* Take the IOM lock before performing any MMIO. */
1860 VBOXSTRICTRC rc = IOM_LOCK_SHARED(pVM);
1861#ifndef IN_RING3
1862 if (rc == VERR_SEM_BUSY)
1863 return VINF_IOM_R3_MMIO_WRITE;
1864#endif
1865 AssertRC(VBOXSTRICTRC_VAL(rc));
1866#if defined(IEM_VERIFICATION_MODE) && defined(IN_RING3)
1867 IEMNotifyMMIORead(pVM, GCPhys, cbValue);
1868#endif
1869
1870 /*
1871 * Lookup the current context range node and statistics.
1872 */
1873 PIOMMMIORANGE pRange = iomMmioGetRange(pVM, pVCpu, GCPhys);
1874 if (!pRange)
1875 {
1876 AssertMsgFailed(("Handlers and page tables are out of sync or something! GCPhys=%RGp cbValue=%d\n", GCPhys, cbValue));
1877 IOM_UNLOCK_SHARED(pVM);
1878 return VERR_IOM_MMIO_RANGE_NOT_FOUND;
1879 }
1880 iomMmioRetainRange(pRange);
1881#ifndef VBOX_WITH_STATISTICS
1882 IOM_UNLOCK_SHARED(pVM);
1883
1884#else /* VBOX_WITH_STATISTICS */
1885 PIOMMMIOSTATS pStats = iomMmioGetStats(pVM, pVCpu, GCPhys, pRange);
1886 if (!pStats)
1887 {
1888 iomMmioReleaseRange(pVM, pRange);
1889# ifdef IN_RING3
1890 return VERR_NO_MEMORY;
1891# else
1892 return VINF_IOM_R3_MMIO_READ;
1893# endif
1894 }
1895 STAM_COUNTER_INC(&pStats->Accesses);
1896#endif /* VBOX_WITH_STATISTICS */
1897
1898 if (pRange->CTX_SUFF(pfnReadCallback))
1899 {
1900 /*
1901 * Perform locking.
1902 */
1903 PPDMDEVINS pDevIns = pRange->CTX_SUFF(pDevIns);
1904 rc = PDMCritSectEnter(pDevIns->CTX_SUFF(pCritSectRo), VINF_IOM_R3_MMIO_WRITE);
1905 if (rc != VINF_SUCCESS)
1906 {
1907 iomMmioReleaseRange(pVM, pRange);
1908 return rc;
1909 }
1910
1911 /*
1912 * Perform the read and deal with the result.
1913 */
1914 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfRead), a);
1915 if ( (cbValue == 4 && !(GCPhys & 3))
1916 || (pRange->fFlags & IOMMMIO_FLAGS_READ_MODE) == IOMMMIO_FLAGS_READ_PASSTHRU
1917 || (cbValue == 8 && !(GCPhys & 7)) )
1918 rc = pRange->CTX_SUFF(pfnReadCallback)(pRange->CTX_SUFF(pDevIns), pRange->CTX_SUFF(pvUser), GCPhys,
1919 pu32Value, (unsigned)cbValue);
1920 else
1921 rc = iomMMIODoComplicatedRead(pVM, pRange, GCPhys, pu32Value, (unsigned)cbValue);
1922 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfRead), a);
1923 switch (VBOXSTRICTRC_VAL(rc))
1924 {
1925 case VINF_SUCCESS:
1926 Log4(("IOMMMIORead: GCPhys=%RGp *pu32=%08RX32 cb=%d rc=VINF_SUCCESS\n", GCPhys, *pu32Value, cbValue));
1927 PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo));
1928 iomMmioReleaseRange(pVM, pRange);
1929 return rc;
1930#ifndef IN_RING3
1931 case VINF_IOM_R3_MMIO_READ:
1932 case VINF_IOM_R3_MMIO_READ_WRITE:
1933 STAM_COUNTER_INC(&pStats->CTX_MID_Z(Read,ToR3));
1934#endif
1935 default:
1936 Log4(("IOMMMIORead: GCPhys=%RGp *pu32=%08RX32 cb=%d rc=%Rrc\n", GCPhys, *pu32Value, cbValue, VBOXSTRICTRC_VAL(rc)));
1937 PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo));
1938 iomMmioReleaseRange(pVM, pRange);
1939 return rc;
1940
1941 case VINF_IOM_MMIO_UNUSED_00:
1942 iomMMIODoRead00s(pu32Value, cbValue);
1943 Log4(("IOMMMIORead: GCPhys=%RGp *pu32=%08RX32 cb=%d rc=%Rrc\n", GCPhys, *pu32Value, cbValue, VBOXSTRICTRC_VAL(rc)));
1944 PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo));
1945 iomMmioReleaseRange(pVM, pRange);
1946 return VINF_SUCCESS;
1947
1948 case VINF_IOM_MMIO_UNUSED_FF:
1949 iomMMIODoReadFFs(pu32Value, cbValue);
1950 Log4(("IOMMMIORead: GCPhys=%RGp *pu32=%08RX32 cb=%d rc=%Rrc\n", GCPhys, *pu32Value, cbValue, VBOXSTRICTRC_VAL(rc)));
1951 PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo));
1952 iomMmioReleaseRange(pVM, pRange);
1953 return VINF_SUCCESS;
1954 }
1955 /* not reached */
1956 }
1957#ifndef IN_RING3
1958 if (pRange->pfnReadCallbackR3)
1959 {
1960 STAM_COUNTER_INC(&pStats->CTX_MID_Z(Read,ToR3));
1961 iomMmioReleaseRange(pVM, pRange);
1962 return VINF_IOM_R3_MMIO_READ;
1963 }
1964#endif
1965
1966 /*
1967 * Unassigned memory - this is actually not supposed t happen...
1968 */
1969 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfRead), a); /** @todo STAM_PROFILE_ADD_ZERO_PERIOD */
1970 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfRead), a);
1971 iomMMIODoReadFFs(pu32Value, cbValue);
1972 Log4(("IOMMMIORead: GCPhys=%RGp *pu32=%08RX32 cb=%d rc=VINF_SUCCESS\n", GCPhys, *pu32Value, cbValue));
1973 iomMmioReleaseRange(pVM, pRange);
1974 return VINF_SUCCESS;
1975}
1976
1977
1978/**
1979 * Writes to a MMIO register.
1980 *
1981 * @returns VBox status code.
1982 *
1983 * @param pVM Pointer to the VM.
1984 * @param pVCpu Pointer to the virtual CPU structure of the caller.
1985 * @param GCPhys The physical address to write to.
1986 * @param u32Value The value to write.
1987 * @param cbValue The size of the register to read in bytes. 1, 2 or 4 bytes.
1988 */
1989VMMDECL(VBOXSTRICTRC) IOMMMIOWrite(PVM pVM, PVMCPU pVCpu, RTGCPHYS GCPhys, uint32_t u32Value, size_t cbValue)
1990{
1991 /* Take the IOM lock before performing any MMIO. */
1992 VBOXSTRICTRC rc = IOM_LOCK_SHARED(pVM);
1993#ifndef IN_RING3
1994 if (rc == VERR_SEM_BUSY)
1995 return VINF_IOM_R3_MMIO_WRITE;
1996#endif
1997 AssertRC(VBOXSTRICTRC_VAL(rc));
1998#if defined(IEM_VERIFICATION_MODE) && defined(IN_RING3)
1999 IEMNotifyMMIOWrite(pVM, GCPhys, u32Value, cbValue);
2000#endif
2001
2002 /*
2003 * Lookup the current context range node.
2004 */
2005 PIOMMMIORANGE pRange = iomMmioGetRange(pVM, pVCpu, GCPhys);
2006 if (!pRange)
2007 {
2008 AssertMsgFailed(("Handlers and page tables are out of sync or something! GCPhys=%RGp cbValue=%d\n", GCPhys, cbValue));
2009 IOM_UNLOCK_SHARED(pVM);
2010 return VERR_IOM_MMIO_RANGE_NOT_FOUND;
2011 }
2012 iomMmioRetainRange(pRange);
2013#ifndef VBOX_WITH_STATISTICS
2014 IOM_UNLOCK_SHARED(pVM);
2015
2016#else /* VBOX_WITH_STATISTICS */
2017 PIOMMMIOSTATS pStats = iomMmioGetStats(pVM, pVCpu, GCPhys, pRange);
2018 if (!pStats)
2019 {
2020 iomMmioReleaseRange(pVM, pRange);
2021# ifdef IN_RING3
2022 return VERR_NO_MEMORY;
2023# else
2024 return VINF_IOM_R3_MMIO_WRITE;
2025# endif
2026 }
2027 STAM_COUNTER_INC(&pStats->Accesses);
2028#endif /* VBOX_WITH_STATISTICS */
2029
2030 if (pRange->CTX_SUFF(pfnWriteCallback))
2031 {
2032 /*
2033 * Perform locking.
2034 */
2035 PPDMDEVINS pDevIns = pRange->CTX_SUFF(pDevIns);
2036 rc = PDMCritSectEnter(pDevIns->CTX_SUFF(pCritSectRo), VINF_IOM_R3_MMIO_READ);
2037 if (rc != VINF_SUCCESS)
2038 {
2039 iomMmioReleaseRange(pVM, pRange);
2040 return rc;
2041 }
2042
2043 /*
2044 * Perform the write.
2045 */
2046 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfWrite), a);
2047 if ( (cbValue == 4 && !(GCPhys & 3))
2048 || (pRange->fFlags & IOMMMIO_FLAGS_WRITE_MODE) == IOMMMIO_FLAGS_WRITE_PASSTHRU
2049 || (cbValue == 8 && !(GCPhys & 7)) )
2050 rc = pRange->CTX_SUFF(pfnWriteCallback)(pRange->CTX_SUFF(pDevIns), pRange->CTX_SUFF(pvUser),
2051 GCPhys, &u32Value, (unsigned)cbValue);
2052 else
2053 rc = iomMMIODoComplicatedWrite(pVM, pRange, GCPhys, &u32Value, (unsigned)cbValue);
2054 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfWrite), a);
2055#ifndef IN_RING3
2056 if ( rc == VINF_IOM_R3_MMIO_WRITE
2057 || rc == VINF_IOM_R3_MMIO_READ_WRITE)
2058 STAM_COUNTER_INC(&pStats->CTX_MID_Z(Write,ToR3));
2059#endif
2060 Log4(("IOMMMIOWrite: GCPhys=%RGp u32=%08RX32 cb=%d rc=%Rrc\n", GCPhys, u32Value, cbValue, VBOXSTRICTRC_VAL(rc)));
2061 iomMmioReleaseRange(pVM, pRange);
2062 PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo));
2063 return rc;
2064 }
2065#ifndef IN_RING3
2066 if (pRange->pfnWriteCallbackR3)
2067 {
2068 STAM_COUNTER_INC(&pStats->CTX_MID_Z(Write,ToR3));
2069 iomMmioReleaseRange(pVM, pRange);
2070 return VINF_IOM_R3_MMIO_WRITE;
2071 }
2072#endif
2073
2074 /*
2075 * No write handler, nothing to do.
2076 */
2077 STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfWrite), a);
2078 STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfWrite), a);
2079 Log4(("IOMMMIOWrite: GCPhys=%RGp u32=%08RX32 cb=%d rc=%Rrc\n", GCPhys, u32Value, cbValue, VINF_SUCCESS));
2080 iomMmioReleaseRange(pVM, pRange);
2081 return VINF_SUCCESS;
2082}
2083
2084
2085/**
2086 * [REP*] INSB/INSW/INSD
2087 * ES:EDI,DX[,ECX]
2088 *
2089 * @remark Assumes caller checked the access privileges (IOMInterpretCheckPortIOAccess)
2090 *
2091 * @returns Strict VBox status code. Informational status codes other than the one documented
2092 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
2093 * @retval VINF_SUCCESS Success.
2094 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
2095 * status code must be passed on to EM.
2096 * @retval VINF_IOM_R3_IOPORT_READ Defer the read to ring-3. (R0/GC only)
2097 * @retval VINF_EM_RAW_EMULATE_INSTR Defer the read to the REM.
2098 * @retval VINF_EM_RAW_GUEST_TRAP The exception was left pending. (TRPMRaiseXcptErr)
2099 * @retval VINF_TRPM_XCPT_DISPATCHED The exception was raised and dispatched for raw-mode execution. (TRPMRaiseXcptErr)
2100 * @retval VINF_EM_RESCHEDULE_REM The exception was dispatched and cannot be executed in raw-mode. (TRPMRaiseXcptErr)
2101 *
2102 * @param pVM The virtual machine.
2103 * @param pVCpu Pointer to the virtual CPU structure of the caller.
2104 * @param pRegFrame Pointer to CPUMCTXCORE guest registers structure.
2105 * @param uPort IO Port
2106 * @param uPrefix IO instruction prefix
2107 * @param enmAddrMode The address mode.
2108 * @param cbTransfer Size of transfer unit
2109 */
2110VMMDECL(VBOXSTRICTRC) IOMInterpretINSEx(PVM pVM, PVMCPU pVCpu, PCPUMCTXCORE pRegFrame, uint32_t uPort, uint32_t uPrefix,
2111 DISCPUMODE enmAddrMode, uint32_t cbTransfer)
2112{
2113 STAM_COUNTER_INC(&pVM->iom.s.StatInstIns);
2114
2115 /*
2116 * We do not support REPNE or decrementing destination
2117 * pointer. Segment prefixes are deliberately ignored, as per the instruction specification.
2118 */
2119 if ( (uPrefix & DISPREFIX_REPNE)
2120 || pRegFrame->eflags.Bits.u1DF)
2121 return VINF_EM_RAW_EMULATE_INSTR;
2122
2123 /*
2124 * Get bytes/words/dwords count to transfer.
2125 */
2126 uint64_t const fAddrMask = iomDisModeToMask(enmAddrMode);
2127 RTGCUINTREG cTransfers = 1;
2128 if (uPrefix & DISPREFIX_REP)
2129 {
2130#ifndef IN_RC
2131 if ( CPUMIsGuestIn64BitCode(pVCpu)
2132 && pRegFrame->rcx >= _4G)
2133 return VINF_EM_RAW_EMULATE_INSTR;
2134#endif
2135 cTransfers = pRegFrame->rcx & fAddrMask;
2136 if (!cTransfers)
2137 return VINF_SUCCESS;
2138 }
2139
2140 /* Convert destination address es:edi. */
2141 RTGCPTR GCPtrDst;
2142 int rc2 = SELMToFlatEx(pVCpu, DISSELREG_ES, pRegFrame, pRegFrame->rdi & fAddrMask,
2143 SELMTOFLAT_FLAGS_HYPER | SELMTOFLAT_FLAGS_NO_PL,
2144 &GCPtrDst);
2145 if (RT_FAILURE(rc2))
2146 {
2147 Log(("INS destination address conversion failed -> fallback, rc2=%d\n", rc2));
2148 return VINF_EM_RAW_EMULATE_INSTR;
2149 }
2150
2151 /* Access verification first; we can't recover from traps inside this instruction, as the port read cannot be repeated. */
2152 uint32_t const cpl = CPUMGetGuestCPL(pVCpu);
2153 rc2 = PGMVerifyAccess(pVCpu, (RTGCUINTPTR)GCPtrDst, cTransfers * cbTransfer,
2154 X86_PTE_RW | ((cpl == 3) ? X86_PTE_US : 0));
2155 if (rc2 != VINF_SUCCESS)
2156 {
2157 Log(("INS will generate a trap -> fallback, rc2=%d\n", rc2));
2158 return VINF_EM_RAW_EMULATE_INSTR;
2159 }
2160
2161 Log(("IOM: rep ins%d port %#x count %d\n", cbTransfer * 8, uPort, cTransfers));
2162 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
2163 if (cTransfers > 1)
2164 {
2165 /* If the device supports string transfers, ask it to do as
2166 * much as it wants. The rest is done with single-word transfers. */
2167 const RTGCUINTREG cTransfersOrg = cTransfers;
2168 rcStrict = IOMIOPortReadString(pVM, pVCpu, uPort, &GCPtrDst, &cTransfers, cbTransfer);
2169 AssertRC(VBOXSTRICTRC_VAL(rcStrict)); Assert(cTransfers <= cTransfersOrg);
2170 pRegFrame->rdi = ((pRegFrame->rdi + (cTransfersOrg - cTransfers) * cbTransfer) & fAddrMask)
2171 | (pRegFrame->rdi & ~fAddrMask);
2172 }
2173
2174#ifdef IN_RC
2175 MMGCRamRegisterTrapHandler(pVM);
2176#endif
2177 while (cTransfers && rcStrict == VINF_SUCCESS)
2178 {
2179 uint32_t u32Value;
2180 rcStrict = IOMIOPortRead(pVM, pVCpu, uPort, &u32Value, cbTransfer);
2181 if (!IOM_SUCCESS(rcStrict))
2182 break;
2183 rc2 = iomRamWrite(pVCpu, pRegFrame, GCPtrDst, &u32Value, cbTransfer);
2184 Assert(rc2 == VINF_SUCCESS); NOREF(rc2);
2185 GCPtrDst = (RTGCPTR)((RTGCUINTPTR)GCPtrDst + cbTransfer);
2186 pRegFrame->rdi = ((pRegFrame->rdi + cbTransfer) & fAddrMask)
2187 | (pRegFrame->rdi & ~fAddrMask);
2188 cTransfers--;
2189 }
2190#ifdef IN_RC
2191 MMGCRamDeregisterTrapHandler(pVM);
2192#endif
2193
2194 /* Update rcx on exit. */
2195 if (uPrefix & DISPREFIX_REP)
2196 pRegFrame->rcx = (cTransfers & fAddrMask)
2197 | (pRegFrame->rcx & ~fAddrMask);
2198
2199 AssertMsg(rcStrict == VINF_SUCCESS || rcStrict == VINF_IOM_R3_IOPORT_READ || (rcStrict >= VINF_EM_FIRST && rcStrict <= VINF_EM_LAST) || RT_FAILURE(rcStrict), ("%Rrc\n", VBOXSTRICTRC_VAL(rcStrict)));
2200 return rcStrict;
2201}
2202
2203
2204#if !defined(VBOX_WITH_FIRST_IEM_STEP) || defined(IN_RC) /* Latter for IOMRCIOPortHandler */
2205/**
2206 * [REP*] INSB/INSW/INSD
2207 * ES:EDI,DX[,ECX]
2208 *
2209 * @returns Strict VBox status code. Informational status codes other than the one documented
2210 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
2211 * @retval VINF_SUCCESS Success.
2212 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
2213 * status code must be passed on to EM.
2214 * @retval VINF_IOM_R3_IOPORT_READ Defer the read to ring-3. (R0/GC only)
2215 * @retval VINF_EM_RAW_EMULATE_INSTR Defer the read to the REM.
2216 * @retval VINF_EM_RAW_GUEST_TRAP The exception was left pending. (TRPMRaiseXcptErr)
2217 * @retval VINF_TRPM_XCPT_DISPATCHED The exception was raised and dispatched for raw-mode execution. (TRPMRaiseXcptErr)
2218 * @retval VINF_EM_RESCHEDULE_REM The exception was dispatched and cannot be executed in raw-mode. (TRPMRaiseXcptErr)
2219 *
2220 * @param pVM The virtual machine.
2221 * @param pVCpu Pointer to the virtual CPU structure of the caller.
2222 * @param pRegFrame Pointer to CPUMCTXCORE guest registers structure.
2223 * @param pCpu Disassembler CPU state.
2224 */
2225VMMDECL(VBOXSTRICTRC) IOMInterpretINS(PVM pVM, PVMCPU pVCpu, PCPUMCTXCORE pRegFrame, PDISCPUSTATE pCpu)
2226{
2227 /*
2228 * Get port number directly from the register (no need to bother the
2229 * disassembler). And get the I/O register size from the opcode / prefix.
2230 */
2231 RTIOPORT Port = pRegFrame->edx & 0xffff;
2232 unsigned cb = 0;
2233 if (pCpu->pCurInstr->uOpcode == OP_INSB)
2234 cb = 1;
2235 else
2236 cb = (pCpu->uOpMode == DISCPUMODE_16BIT) ? 2 : 4; /* dword in both 32 & 64 bits mode */
2237
2238 VBOXSTRICTRC rcStrict = IOMInterpretCheckPortIOAccess(pVM, pRegFrame, Port, cb);
2239 if (RT_UNLIKELY(rcStrict != VINF_SUCCESS))
2240 {
2241 AssertMsg(rcStrict == VINF_EM_RAW_GUEST_TRAP || rcStrict == VINF_TRPM_XCPT_DISPATCHED || rcStrict == VINF_TRPM_XCPT_DISPATCHED || RT_FAILURE(rcStrict), ("%Rrc\n", VBOXSTRICTRC_VAL(rcStrict)));
2242 return rcStrict;
2243 }
2244
2245 return IOMInterpretINSEx(pVM, pVCpu, pRegFrame, Port, pCpu->fPrefix, (DISCPUMODE)pCpu->uAddrMode, cb);
2246}
2247#endif /* !IEM || RC */
2248
2249
2250/**
2251 * [REP*] OUTSB/OUTSW/OUTSD
2252 * DS:ESI,DX[,ECX]
2253 *
2254 * @remark Assumes caller checked the access privileges (IOMInterpretCheckPortIOAccess)
2255 *
2256 * @returns Strict VBox status code. Informational status codes other than the one documented
2257 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
2258 * @retval VINF_SUCCESS Success.
2259 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
2260 * status code must be passed on to EM.
2261 * @retval VINF_IOM_R3_IOPORT_WRITE Defer the write to ring-3. (R0/GC only)
2262 * @retval VINF_EM_RAW_GUEST_TRAP The exception was left pending. (TRPMRaiseXcptErr)
2263 * @retval VINF_TRPM_XCPT_DISPATCHED The exception was raised and dispatched for raw-mode execution. (TRPMRaiseXcptErr)
2264 * @retval VINF_EM_RESCHEDULE_REM The exception was dispatched and cannot be executed in raw-mode. (TRPMRaiseXcptErr)
2265 *
2266 * @param pVM The virtual machine.
2267 * @param pVCpu Pointer to the virtual CPU structure of the caller.
2268 * @param pRegFrame Pointer to CPUMCTXCORE guest registers structure.
2269 * @param uPort IO Port
2270 * @param uPrefix IO instruction prefix
2271 * @param enmAddrMode The address mode.
2272 * @param cbTransfer Size of transfer unit
2273 */
2274VMMDECL(VBOXSTRICTRC) IOMInterpretOUTSEx(PVM pVM, PVMCPU pVCpu, PCPUMCTXCORE pRegFrame, uint32_t uPort, uint32_t uPrefix,
2275 DISCPUMODE enmAddrMode, uint32_t cbTransfer)
2276{
2277 STAM_COUNTER_INC(&pVM->iom.s.StatInstOuts);
2278
2279 /*
2280 * We do not support segment prefixes, REPNE or
2281 * decrementing source pointer.
2282 */
2283 if ( (uPrefix & (DISPREFIX_SEG | DISPREFIX_REPNE))
2284 || pRegFrame->eflags.Bits.u1DF)
2285 return VINF_EM_RAW_EMULATE_INSTR;
2286
2287 /*
2288 * Get bytes/words/dwords count to transfer.
2289 */
2290 uint64_t const fAddrMask = iomDisModeToMask(enmAddrMode);
2291 RTGCUINTREG cTransfers = 1;
2292 if (uPrefix & DISPREFIX_REP)
2293 {
2294#ifndef IN_RC
2295 if ( CPUMIsGuestIn64BitCode(pVCpu)
2296 && pRegFrame->rcx >= _4G)
2297 return VINF_EM_RAW_EMULATE_INSTR;
2298#endif
2299 cTransfers = pRegFrame->rcx & fAddrMask;
2300 if (!cTransfers)
2301 return VINF_SUCCESS;
2302 }
2303
2304 /* Convert source address ds:esi. */
2305 RTGCPTR GCPtrSrc;
2306 int rc2 = SELMToFlatEx(pVCpu, DISSELREG_DS, pRegFrame, pRegFrame->rsi & fAddrMask,
2307 SELMTOFLAT_FLAGS_HYPER | SELMTOFLAT_FLAGS_NO_PL,
2308 &GCPtrSrc);
2309 if (RT_FAILURE(rc2))
2310 {
2311 Log(("OUTS source address conversion failed -> fallback, rc2=%Rrc\n", rc2));
2312 return VINF_EM_RAW_EMULATE_INSTR;
2313 }
2314
2315 /* Access verification first; we currently can't recover properly from traps inside this instruction */
2316 uint32_t const cpl = CPUMGetGuestCPL(pVCpu);
2317 rc2 = PGMVerifyAccess(pVCpu, (RTGCUINTPTR)GCPtrSrc, cTransfers * cbTransfer,
2318 (cpl == 3) ? X86_PTE_US : 0);
2319 if (rc2 != VINF_SUCCESS)
2320 {
2321 Log(("OUTS will generate a trap -> fallback, rc2=%Rrc\n", rc2));
2322 return VINF_EM_RAW_EMULATE_INSTR;
2323 }
2324
2325 Log(("IOM: rep outs%d port %#x count %d\n", cbTransfer * 8, uPort, cTransfers));
2326 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
2327 if (cTransfers > 1)
2328 {
2329 /*
2330 * If the device supports string transfers, ask it to do as
2331 * much as it wants. The rest is done with single-word transfers.
2332 */
2333 const RTGCUINTREG cTransfersOrg = cTransfers;
2334 rcStrict = IOMIOPortWriteString(pVM, pVCpu, uPort, &GCPtrSrc, &cTransfers, cbTransfer);
2335 AssertRC(VBOXSTRICTRC_VAL(rcStrict)); Assert(cTransfers <= cTransfersOrg);
2336 pRegFrame->rsi = ((pRegFrame->rsi + (cTransfersOrg - cTransfers) * cbTransfer) & fAddrMask)
2337 | (pRegFrame->rsi & ~fAddrMask);
2338 }
2339
2340#ifdef IN_RC
2341 MMGCRamRegisterTrapHandler(pVM);
2342#endif
2343
2344 while (cTransfers && rcStrict == VINF_SUCCESS)
2345 {
2346 uint32_t u32Value = 0;
2347 rcStrict = iomRamRead(pVCpu, &u32Value, GCPtrSrc, cbTransfer);
2348 if (rcStrict != VINF_SUCCESS)
2349 break;
2350 rcStrict = IOMIOPortWrite(pVM, pVCpu, uPort, u32Value, cbTransfer);
2351 if (!IOM_SUCCESS(rcStrict))
2352 break;
2353 GCPtrSrc = (RTGCPTR)((RTUINTPTR)GCPtrSrc + cbTransfer);
2354 pRegFrame->rsi = ((pRegFrame->rsi + cbTransfer) & fAddrMask)
2355 | (pRegFrame->rsi & ~fAddrMask);
2356 cTransfers--;
2357 }
2358
2359#ifdef IN_RC
2360 MMGCRamDeregisterTrapHandler(pVM);
2361#endif
2362
2363 /* Update rcx on exit. */
2364 if (uPrefix & DISPREFIX_REP)
2365 pRegFrame->rcx = (cTransfers & fAddrMask)
2366 | (pRegFrame->rcx & ~fAddrMask);
2367
2368 AssertMsg(rcStrict == VINF_SUCCESS || rcStrict == VINF_IOM_R3_IOPORT_WRITE || (rcStrict >= VINF_EM_FIRST && rcStrict <= VINF_EM_LAST) || RT_FAILURE(rcStrict), ("%Rrc\n", VBOXSTRICTRC_VAL(rcStrict)));
2369 return rcStrict;
2370}
2371
2372
2373#if !defined(VBOX_WITH_FIRST_IEM_STEP) || defined(IN_RC) /* Latter for IOMRCIOPortHandler */
2374/**
2375 * [REP*] OUTSB/OUTSW/OUTSD
2376 * DS:ESI,DX[,ECX]
2377 *
2378 * @returns Strict VBox status code. Informational status codes other than the one documented
2379 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
2380 * @retval VINF_SUCCESS Success.
2381 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
2382 * status code must be passed on to EM.
2383 * @retval VINF_IOM_R3_IOPORT_WRITE Defer the write to ring-3. (R0/GC only)
2384 * @retval VINF_EM_RAW_EMULATE_INSTR Defer the write to the REM.
2385 * @retval VINF_EM_RAW_GUEST_TRAP The exception was left pending. (TRPMRaiseXcptErr)
2386 * @retval VINF_TRPM_XCPT_DISPATCHED The exception was raised and dispatched for raw-mode execution. (TRPMRaiseXcptErr)
2387 * @retval VINF_EM_RESCHEDULE_REM The exception was dispatched and cannot be executed in raw-mode. (TRPMRaiseXcptErr)
2388 *
2389 * @param pVM The virtual machine.
2390 * @param pVCpu Pointer to the virtual CPU structure of the caller.
2391 * @param pRegFrame Pointer to CPUMCTXCORE guest registers structure.
2392 * @param pCpu Disassembler CPU state.
2393 */
2394VMMDECL(VBOXSTRICTRC) IOMInterpretOUTS(PVM pVM, PVMCPU pVCpu, PCPUMCTXCORE pRegFrame, PDISCPUSTATE pCpu)
2395{
2396 /*
2397 * Get port number from the first parameter.
2398 * And get the I/O register size from the opcode / prefix.
2399 */
2400 uint64_t Port = 0;
2401 unsigned cb = 0;
2402 bool fRc = iomGetRegImmData(pCpu, &pCpu->Param1, pRegFrame, &Port, &cb);
2403 AssertMsg(fRc, ("Failed to get reg/imm port number!\n")); NOREF(fRc);
2404 if (pCpu->pCurInstr->uOpcode == OP_OUTSB)
2405 cb = 1;
2406 else
2407 cb = (pCpu->uOpMode == DISCPUMODE_16BIT) ? 2 : 4; /* dword in both 32 & 64 bits mode */
2408
2409 VBOXSTRICTRC rcStrict = IOMInterpretCheckPortIOAccess(pVM, pRegFrame, Port, cb);
2410 if (RT_UNLIKELY(rcStrict != VINF_SUCCESS))
2411 {
2412 AssertMsg(rcStrict == VINF_EM_RAW_GUEST_TRAP || rcStrict == VINF_TRPM_XCPT_DISPATCHED || rcStrict == VINF_TRPM_XCPT_DISPATCHED || RT_FAILURE(rcStrict), ("%Rrc\n", VBOXSTRICTRC_VAL(rcStrict)));
2413 return rcStrict;
2414 }
2415
2416 return IOMInterpretOUTSEx(pVM, pVCpu, pRegFrame, Port, pCpu->fPrefix, (DISCPUMODE)pCpu->uAddrMode, cb);
2417}
2418#endif /* !IEM || RC */
2419
2420#ifndef IN_RC
2421
2422/**
2423 * Mapping an MMIO2 page in place of an MMIO page for direct access.
2424 *
2425 * (This is a special optimization used by the VGA device.)
2426 *
2427 * @returns VBox status code. This API may return VINF_SUCCESS even if no
2428 * remapping is made,.
2429 *
2430 * @param pVM The virtual machine.
2431 * @param GCPhys The address of the MMIO page to be changed.
2432 * @param GCPhysRemapped The address of the MMIO2 page.
2433 * @param fPageFlags Page flags to set. Must be (X86_PTE_RW | X86_PTE_P)
2434 * for the time being.
2435 */
2436VMMDECL(int) IOMMMIOMapMMIO2Page(PVM pVM, RTGCPHYS GCPhys, RTGCPHYS GCPhysRemapped, uint64_t fPageFlags)
2437{
2438# ifndef IEM_VERIFICATION_MODE_FULL
2439 /* Currently only called from the VGA device during MMIO. */
2440 Log(("IOMMMIOMapMMIO2Page %RGp -> %RGp flags=%RX64\n", GCPhys, GCPhysRemapped, fPageFlags));
2441 AssertReturn(fPageFlags == (X86_PTE_RW | X86_PTE_P), VERR_INVALID_PARAMETER);
2442 PVMCPU pVCpu = VMMGetCpu(pVM);
2443
2444 /* This currently only works in real mode, protected mode without paging or with nested paging. */
2445 if ( !HMIsEnabled(pVM) /* useless without VT-x/AMD-V */
2446 || ( CPUMIsGuestInPagedProtectedMode(pVCpu)
2447 && !HMIsNestedPagingActive(pVM)))
2448 return VINF_SUCCESS; /* ignore */
2449
2450 int rc = IOM_LOCK_SHARED(pVM);
2451 if (RT_FAILURE(rc))
2452 return VINF_SUCCESS; /* better luck the next time around */
2453
2454 /*
2455 * Lookup the context range node the page belongs to.
2456 */
2457 PIOMMMIORANGE pRange = iomMmioGetRange(pVM, pVCpu, GCPhys);
2458 AssertMsgReturn(pRange,
2459 ("Handlers and page tables are out of sync or something! GCPhys=%RGp\n", GCPhys), VERR_IOM_MMIO_RANGE_NOT_FOUND);
2460
2461 Assert((pRange->GCPhys & PAGE_OFFSET_MASK) == 0);
2462 Assert((pRange->Core.KeyLast & PAGE_OFFSET_MASK) == PAGE_OFFSET_MASK);
2463
2464 /*
2465 * Do the aliasing; page align the addresses since PGM is picky.
2466 */
2467 GCPhys &= ~(RTGCPHYS)PAGE_OFFSET_MASK;
2468 GCPhysRemapped &= ~(RTGCPHYS)PAGE_OFFSET_MASK;
2469
2470 rc = PGMHandlerPhysicalPageAlias(pVM, pRange->GCPhys, GCPhys, GCPhysRemapped);
2471
2472 IOM_UNLOCK_SHARED(pVM);
2473 AssertRCReturn(rc, rc);
2474
2475 /*
2476 * Modify the shadow page table. Since it's an MMIO page it won't be present and we
2477 * can simply prefetch it.
2478 *
2479 * Note: This is a NOP in the EPT case; we'll just let it fault again to resync the page.
2480 */
2481# if 0 /* The assertion is wrong for the PGM_SYNC_CLEAR_PGM_POOL and VINF_PGM_HANDLER_ALREADY_ALIASED cases. */
2482# ifdef VBOX_STRICT
2483 uint64_t fFlags;
2484 RTHCPHYS HCPhys;
2485 rc = PGMShwGetPage(pVCpu, (RTGCPTR)GCPhys, &fFlags, &HCPhys);
2486 Assert(rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
2487# endif
2488# endif
2489 rc = PGMPrefetchPage(pVCpu, (RTGCPTR)GCPhys);
2490 Assert(rc == VINF_SUCCESS || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
2491# endif /* !IEM_VERIFICATION_MODE_FULL */
2492 return VINF_SUCCESS;
2493}
2494
2495
2496# ifndef IEM_VERIFICATION_MODE_FULL
2497/**
2498 * Mapping a HC page in place of an MMIO page for direct access.
2499 *
2500 * (This is a special optimization used by the APIC in the VT-x case.)
2501 *
2502 * @returns VBox status code.
2503 *
2504 * @param pVM Pointer to the VM.
2505 * @param pVCpu Pointer to the VMCPU.
2506 * @param GCPhys The address of the MMIO page to be changed.
2507 * @param HCPhys The address of the host physical page.
2508 * @param fPageFlags Page flags to set. Must be (X86_PTE_RW | X86_PTE_P)
2509 * for the time being.
2510 */
2511VMMDECL(int) IOMMMIOMapMMIOHCPage(PVM pVM, PVMCPU pVCpu, RTGCPHYS GCPhys, RTHCPHYS HCPhys, uint64_t fPageFlags)
2512{
2513 /* Currently only called from VT-x code during a page fault. */
2514 Log(("IOMMMIOMapMMIOHCPage %RGp -> %RGp flags=%RX64\n", GCPhys, HCPhys, fPageFlags));
2515
2516 AssertReturn(fPageFlags == (X86_PTE_RW | X86_PTE_P), VERR_INVALID_PARAMETER);
2517 Assert(HMIsEnabled(pVM));
2518
2519 /*
2520 * Lookup the context range node the page belongs to.
2521 */
2522#ifdef VBOX_STRICT
2523 /* Can't lock IOM here due to potential deadlocks in the VGA device; not safe to access. */
2524 PIOMMMIORANGE pRange = iomMMIOGetRangeUnsafe(pVM, pVCpu, GCPhys);
2525 AssertMsgReturn(pRange,
2526 ("Handlers and page tables are out of sync or something! GCPhys=%RGp\n", GCPhys), VERR_IOM_MMIO_RANGE_NOT_FOUND);
2527 Assert((pRange->GCPhys & PAGE_OFFSET_MASK) == 0);
2528 Assert((pRange->Core.KeyLast & PAGE_OFFSET_MASK) == PAGE_OFFSET_MASK);
2529#endif
2530
2531 /*
2532 * Do the aliasing; page align the addresses since PGM is picky.
2533 */
2534 GCPhys &= ~(RTGCPHYS)PAGE_OFFSET_MASK;
2535 HCPhys &= ~(RTHCPHYS)PAGE_OFFSET_MASK;
2536
2537 int rc = PGMHandlerPhysicalPageAliasHC(pVM, GCPhys, GCPhys, HCPhys);
2538 AssertRCReturn(rc, rc);
2539
2540 /*
2541 * Modify the shadow page table. Since it's an MMIO page it won't be present and we
2542 * can simply prefetch it.
2543 *
2544 * Note: This is a NOP in the EPT case; we'll just let it fault again to resync the page.
2545 */
2546 rc = PGMPrefetchPage(pVCpu, (RTGCPTR)GCPhys);
2547 Assert(rc == VINF_SUCCESS || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
2548 return VINF_SUCCESS;
2549}
2550#endif /* !IEM_VERIFICATION_MODE_FULL */
2551
2552
2553/**
2554 * Reset a previously modified MMIO region; restore the access flags.
2555 *
2556 * @returns VBox status code.
2557 *
2558 * @param pVM The virtual machine.
2559 * @param GCPhys Physical address that's part of the MMIO region to be reset.
2560 */
2561VMMDECL(int) IOMMMIOResetRegion(PVM pVM, RTGCPHYS GCPhys)
2562{
2563 Log(("IOMMMIOResetRegion %RGp\n", GCPhys));
2564
2565 PVMCPU pVCpu = VMMGetCpu(pVM);
2566
2567 /* This currently only works in real mode, protected mode without paging or with nested paging. */
2568 if ( !HMIsEnabled(pVM) /* useless without VT-x/AMD-V */
2569 || ( CPUMIsGuestInPagedProtectedMode(pVCpu)
2570 && !HMIsNestedPagingActive(pVM)))
2571 return VINF_SUCCESS; /* ignore */
2572
2573 /*
2574 * Lookup the context range node the page belongs to.
2575 */
2576#ifdef VBOX_STRICT
2577 /* Can't lock IOM here due to potential deadlocks in the VGA device; not safe to access. */
2578 PIOMMMIORANGE pRange = iomMMIOGetRangeUnsafe(pVM, pVCpu, GCPhys);
2579 AssertMsgReturn(pRange,
2580 ("Handlers and page tables are out of sync or something! GCPhys=%RGp\n", GCPhys), VERR_IOM_MMIO_RANGE_NOT_FOUND);
2581 Assert((pRange->GCPhys & PAGE_OFFSET_MASK) == 0);
2582 Assert((pRange->Core.KeyLast & PAGE_OFFSET_MASK) == PAGE_OFFSET_MASK);
2583#endif
2584
2585 /*
2586 * Call PGM to do the job work.
2587 *
2588 * After the call, all the pages should be non-present... unless there is
2589 * a page pool flush pending (unlikely).
2590 */
2591 int rc = PGMHandlerPhysicalReset(pVM, GCPhys);
2592 AssertRC(rc);
2593
2594#ifdef VBOX_STRICT
2595 if (!VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_PGM_SYNC_CR3))
2596 {
2597 uint32_t cb = pRange->cb;
2598 GCPhys = pRange->GCPhys;
2599 while (cb)
2600 {
2601 uint64_t fFlags;
2602 RTHCPHYS HCPhys;
2603 rc = PGMShwGetPage(pVCpu, (RTGCPTR)GCPhys, &fFlags, &HCPhys);
2604 Assert(rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
2605 cb -= PAGE_SIZE;
2606 GCPhys += PAGE_SIZE;
2607 }
2608 }
2609#endif
2610 return rc;
2611}
2612
2613#endif /* !IN_RC */
2614
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