VirtualBox

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

Last change on this file since 41692 was 41692, checked in by vboxsync, 12 years ago

DIS: Reducing the DISCPUMODE even more (200 bytes now) and making it have the same layout in all contexts. This is useful since it's used several places in the VM structure. Also a bunch of other cleanups.

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