VirtualBox

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

Last change on this file since 16037 was 16037, checked in by vboxsync, 16 years ago

Don't assert for 8 byte stos/movs

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 62.0 KB
Line 
1/* $Id: IOMAllMMIO.cpp 16037 2009-01-19 10:30:36Z vboxsync $ */
2/** @file
3 * IOM - Input / Output Monitor - Any Context, MMIO & String I/O.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_IOM
27#include <VBox/iom.h>
28#include <VBox/cpum.h>
29#include <VBox/pgm.h>
30#include <VBox/selm.h>
31#include <VBox/mm.h>
32#include <VBox/em.h>
33#include <VBox/pgm.h>
34#include <VBox/trpm.h>
35#include "IOMInternal.h"
36#include <VBox/vm.h>
37#include <VBox/hwaccm.h>
38
39#include <VBox/dis.h>
40#include <VBox/disopcode.h>
41#include <VBox/param.h>
42#include <VBox/err.h>
43#include <iprt/assert.h>
44#include <VBox/log.h>
45#include <iprt/asm.h>
46#include <iprt/string.h>
47
48
49/*******************************************************************************
50* Global Variables *
51*******************************************************************************/
52
53/**
54 * Array for fast recode of the operand size (1/2/4/8 bytes) to bit shift value.
55 */
56static const unsigned g_aSize2Shift[] =
57{
58 ~0, /* 0 - invalid */
59 0, /* *1 == 2^0 */
60 1, /* *2 == 2^1 */
61 ~0, /* 3 - invalid */
62 2, /* *4 == 2^2 */
63 ~0, /* 5 - invalid */
64 ~0, /* 6 - invalid */
65 ~0, /* 7 - invalid */
66 3 /* *8 == 2^3 */
67};
68
69/**
70 * Macro for fast recode of the operand size (1/2/4/8 bytes) to bit shift value.
71 */
72#define SIZE_2_SHIFT(cb) (g_aSize2Shift[cb])
73
74
75/**
76 * Wrapper which does the write and updates range statistics when such are enabled.
77 * @warning RT_SUCCESS(rc=VINF_IOM_HC_MMIO_WRITE) is TRUE!
78 */
79DECLINLINE(int) iomMMIODoWrite(PVM pVM, PIOMMMIORANGE pRange, RTGCPHYS GCPhysFault, const void *pvData, unsigned cb)
80{
81#ifdef VBOX_WITH_STATISTICS
82 PIOMMMIOSTATS pStats = iomMMIOGetStats(&pVM->iom.s, GCPhysFault, pRange);
83 Assert(pStats);
84#endif
85
86 int rc;
87 if (RT_LIKELY(pRange->CTX_SUFF(pfnWriteCallback)))
88 rc = pRange->CTX_SUFF(pfnWriteCallback)(pRange->CTX_SUFF(pDevIns), pRange->CTX_SUFF(pvUser), GCPhysFault, (void *)pvData, cb); /* @todo fix const!! */
89 else
90 rc = VINF_SUCCESS;
91 if (rc != VINF_IOM_HC_MMIO_WRITE)
92 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Write));
93 return rc;
94}
95
96
97/**
98 * Wrapper which does the read and updates range statistics when such are enabled.
99 */
100DECLINLINE(int) iomMMIODoRead(PVM pVM, PIOMMMIORANGE pRange, RTGCPHYS GCPhys, void *pvValue, unsigned cbValue)
101{
102#ifdef VBOX_WITH_STATISTICS
103 PIOMMMIOSTATS pStats = iomMMIOGetStats(&pVM->iom.s, GCPhys, pRange);
104 Assert(pStats);
105#endif
106
107 int rc;
108 if (RT_LIKELY(pRange->CTX_SUFF(pfnReadCallback)))
109 rc = pRange->CTX_SUFF(pfnReadCallback)(pRange->CTX_SUFF(pDevIns), pRange->CTX_SUFF(pvUser), GCPhys, pvValue, cbValue);
110 else
111 rc = VINF_IOM_MMIO_UNUSED_FF;
112 if (rc != VINF_SUCCESS)
113 {
114 switch (rc)
115 {
116 case VINF_IOM_MMIO_UNUSED_FF:
117 switch (cbValue)
118 {
119 case 1: *(uint8_t *)pvValue = UINT8_C(0xff); break;
120 case 2: *(uint16_t *)pvValue = UINT16_C(0xffff); break;
121 case 4: *(uint32_t *)pvValue = UINT32_C(0xffffffff); break;
122 case 8: *(uint64_t *)pvValue = UINT64_C(0xffffffffffffffff); break;
123 default: AssertReleaseMsgFailed(("cbValue=%d GCPhys=%RGp\n", cbValue, GCPhys)); break;
124 }
125 rc = VINF_SUCCESS;
126 break;
127
128 case VINF_IOM_MMIO_UNUSED_00:
129 switch (cbValue)
130 {
131 case 1: *(uint8_t *)pvValue = UINT8_C(0x00); break;
132 case 2: *(uint16_t *)pvValue = UINT16_C(0x0000); break;
133 case 4: *(uint32_t *)pvValue = UINT32_C(0x00000000); break;
134 case 8: *(uint64_t *)pvValue = UINT64_C(0x0000000000000000); break;
135 default: AssertReleaseMsgFailed(("cbValue=%d GCPhys=%RGp\n", cbValue, GCPhys)); break;
136 }
137 rc = VINF_SUCCESS;
138 break;
139 }
140 if (rc != VINF_IOM_HC_MMIO_READ)
141 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Read));
142 }
143 return rc;
144}
145
146
147/**
148 * Internal - statistics only.
149 */
150DECLINLINE(void) iomMMIOStatLength(PVM pVM, unsigned cb)
151{
152#ifdef VBOX_WITH_STATISTICS
153 switch (cb)
154 {
155 case 1:
156 STAM_COUNTER_INC(&pVM->iom.s.StatRZMMIO1Byte);
157 break;
158 case 2:
159 STAM_COUNTER_INC(&pVM->iom.s.StatRZMMIO2Bytes);
160 break;
161 case 4:
162 STAM_COUNTER_INC(&pVM->iom.s.StatRZMMIO4Bytes);
163 break;
164 case 8:
165 STAM_COUNTER_INC(&pVM->iom.s.StatRZMMIO8Bytes);
166 break;
167 default:
168 /* No way. */
169 AssertMsgFailed(("Invalid data length %d\n", cb));
170 break;
171 }
172#else
173 NOREF(pVM); NOREF(cb);
174#endif
175}
176
177
178/**
179 * MOV reg, mem (read)
180 * MOVZX reg, mem (read)
181 * MOVSX reg, mem (read)
182 *
183 * @returns VBox status code.
184 *
185 * @param pVM The virtual machine.
186 * @param pRegFrame Pointer to CPUMCTXCORE guest registers structure.
187 * @param pCpu Disassembler CPU state.
188 * @param pRange Pointer MMIO range.
189 * @param GCPhysFault The GC physical address corresponding to pvFault.
190 */
191static int iomInterpretMOVxXRead(PVM pVM, PCPUMCTXCORE pRegFrame, PDISCPUSTATE pCpu, PIOMMMIORANGE pRange, RTGCPHYS GCPhysFault)
192{
193 Assert(pRange->CTX_SUFF(pfnReadCallback) || !pRange->pfnReadCallbackR3);
194
195 /*
196 * Get the data size from parameter 2,
197 * and call the handler function to get the data.
198 */
199 unsigned cb = DISGetParamSize(pCpu, &pCpu->param2);
200 AssertMsg(cb > 0 && cb <= sizeof(uint64_t), ("cb=%d\n", cb));
201
202 uint64_t u64Data = 0;
203 int rc = iomMMIODoRead(pVM, pRange, GCPhysFault, &u64Data, cb);
204 if (rc == VINF_SUCCESS)
205 {
206 /*
207 * Do sign extension for MOVSX.
208 */
209 /** @todo checkup MOVSX implementation! */
210 if (pCpu->pCurInstr->opcode == OP_MOVSX)
211 {
212 if (cb == 1)
213 {
214 /* DWORD <- BYTE */
215 int64_t iData = (int8_t)u64Data;
216 u64Data = (uint64_t)iData;
217 }
218 else
219 {
220 /* DWORD <- WORD */
221 int64_t iData = (int16_t)u64Data;
222 u64Data = (uint64_t)iData;
223 }
224 }
225
226 /*
227 * Store the result to register (parameter 1).
228 */
229 bool fRc = iomSaveDataToReg(pCpu, &pCpu->param1, pRegFrame, u64Data);
230 AssertMsg(fRc, ("Failed to store register value!\n")); NOREF(fRc);
231 }
232
233 if (rc == VINF_SUCCESS)
234 iomMMIOStatLength(pVM, cb);
235 return rc;
236}
237
238
239/**
240 * MOV mem, reg|imm (write)
241 *
242 * @returns VBox status code.
243 *
244 * @param pVM The virtual machine.
245 * @param pRegFrame Pointer to CPUMCTXCORE guest registers structure.
246 * @param pCpu Disassembler CPU state.
247 * @param pRange Pointer MMIO range.
248 * @param GCPhysFault The GC physical address corresponding to pvFault.
249 */
250static int iomInterpretMOVxXWrite(PVM pVM, PCPUMCTXCORE pRegFrame, PDISCPUSTATE pCpu, PIOMMMIORANGE pRange, RTGCPHYS GCPhysFault)
251{
252 Assert(pRange->CTX_SUFF(pfnWriteCallback) || !pRange->pfnWriteCallbackR3);
253
254 /*
255 * Get data to write from second parameter,
256 * and call the callback to write it.
257 */
258 unsigned cb = 0;
259 uint64_t u64Data = 0;
260 bool fRc = iomGetRegImmData(pCpu, &pCpu->param2, pRegFrame, &u64Data, &cb);
261 AssertMsg(fRc, ("Failed to get reg/imm port number!\n")); NOREF(fRc);
262
263 int rc = iomMMIODoWrite(pVM, pRange, GCPhysFault, &u64Data, cb);
264 if (rc == VINF_SUCCESS)
265 iomMMIOStatLength(pVM, cb);
266 return rc;
267}
268
269
270/** Wrapper for reading virtual memory. */
271DECLINLINE(int) iomRamRead(PVM pVM, void *pDest, RTGCPTR GCSrc, uint32_t cb)
272{
273#ifdef IN_RC
274 return MMGCRamReadNoTrapHandler(pDest, (void *)GCSrc, cb);
275#else
276 return PGMPhysReadGCPtr(pVM, pDest, GCSrc, cb);
277#endif
278}
279
280
281/** Wrapper for writing virtual memory. */
282DECLINLINE(int) iomRamWrite(PVM pVM, RTGCPTR GCDest, void *pSrc, uint32_t cb)
283{
284#ifdef IN_RC
285 return MMGCRamWriteNoTrapHandler((void *)GCDest, pSrc, cb);
286#else
287 return PGMPhysWriteGCPtr(pVM, GCDest, pSrc, cb);
288#endif
289}
290
291
292#ifdef IOM_WITH_MOVS_SUPPORT
293/**
294 * [REP] MOVSB
295 * [REP] MOVSW
296 * [REP] MOVSD
297 *
298 * Restricted implementation.
299 *
300 *
301 * @returns VBox status code.
302 *
303 * @param pVM The virtual machine.
304 * @param uErrorCode CPU Error code.
305 * @param pRegFrame Trap register frame.
306 * @param GCPhysFault The GC physical address corresponding to pvFault.
307 * @param pCpu Disassembler CPU state.
308 * @param pRange Pointer MMIO range.
309 * @param ppStat Which sub-sample to attribute this call to.
310 */
311static int iomInterpretMOVS(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPHYS GCPhysFault, PDISCPUSTATE pCpu, PIOMMMIORANGE pRange, PSTAMPROFILE *ppStat)
312{
313 /*
314 * We do not support segment prefixes or REPNE.
315 */
316 if (pCpu->prefix & (PREFIX_SEG | PREFIX_REPNE))
317 return VINF_IOM_HC_MMIO_READ_WRITE; /** @todo -> interpret whatever. */
318
319
320 /*
321 * Get bytes/words/dwords/qword count to copy.
322 */
323 uint32_t cTransfers = 1;
324 if (pCpu->prefix & PREFIX_REP)
325 {
326#ifndef IN_RC
327 if ( CPUMIsGuestIn64BitCode(pVM, pRegFrame)
328 && pRegFrame->rcx >= _4G)
329 return VINF_EM_RAW_EMULATE_INSTR;
330#endif
331
332 cTransfers = pRegFrame->ecx;
333 if (SELMGetCpuModeFromSelector(pVM, pRegFrame->eflags, pRegFrame->cs, &pRegFrame->csHid) == CPUMODE_16BIT)
334 cTransfers &= 0xffff;
335
336 if (!cTransfers)
337 return VINF_SUCCESS;
338 }
339
340 /* Get the current privilege level. */
341 uint32_t cpl = CPUMGetGuestCPL(pVM, pRegFrame);
342
343 /*
344 * Get data size.
345 */
346 unsigned cb = DISGetParamSize(pCpu, &pCpu->param1);
347 AssertMsg(cb > 0 && cb <= sizeof(uint64_t), ("cb=%d\n", cb));
348 int offIncrement = pRegFrame->eflags.Bits.u1DF ? -(signed)cb : (signed)cb;
349
350#ifdef VBOX_WITH_STATISTICS
351 if (pVM->iom.s.cMovsMaxBytes < (cTransfers << SIZE_2_SHIFT(cb)))
352 pVM->iom.s.cMovsMaxBytes = cTransfers << SIZE_2_SHIFT(cb);
353#endif
354
355/** @todo re-evaluate on page boundraries. */
356
357 RTGCPHYS Phys = GCPhysFault;
358 int rc;
359 if (uErrorCode & X86_TRAP_PF_RW)
360 {
361 /*
362 * Write operation: [Mem] -> [MMIO]
363 * ds:esi (Virt Src) -> es:edi (Phys Dst)
364 */
365 STAM_STATS({ *ppStat = &pVM->iom.s.StatRZInstMovsToMMIO; });
366
367 /* Check callback. */
368 if (!pRange->CTX_SUFF(pfnWriteCallback))
369 return VINF_IOM_HC_MMIO_WRITE;
370
371 /* Convert source address ds:esi. */
372 RTGCUINTPTR pu8Virt;
373 rc = SELMToFlatEx(pVM, DIS_SELREG_DS, pRegFrame, (RTGCPTR)pRegFrame->rsi,
374 SELMTOFLAT_FLAGS_HYPER | SELMTOFLAT_FLAGS_NO_PL,
375 (PRTGCPTR)&pu8Virt);
376 if (RT_SUCCESS(rc))
377 {
378
379 /* Access verification first; we currently can't recover properly from traps inside this instruction */
380 rc = PGMVerifyAccess(pVM, pu8Virt, cTransfers * cb, (cpl == 3) ? X86_PTE_US : 0);
381 if (rc != VINF_SUCCESS)
382 {
383 Log(("MOVS will generate a trap -> recompiler, rc=%d\n", rc));
384 return VINF_EM_RAW_EMULATE_INSTR;
385 }
386
387#ifdef IN_RC
388 MMGCRamRegisterTrapHandler(pVM);
389#endif
390
391 /* copy loop. */
392 while (cTransfers)
393 {
394 uint32_t u32Data = 0;
395 rc = iomRamRead(pVM, &u32Data, (RTGCPTR)pu8Virt, cb);
396 if (rc != VINF_SUCCESS)
397 break;
398 rc = iomMMIODoWrite(pVM, pRange, Phys, &u32Data, cb);
399 if (rc != VINF_SUCCESS)
400 break;
401
402 pu8Virt += offIncrement;
403 Phys += offIncrement;
404 pRegFrame->rsi += offIncrement;
405 pRegFrame->rdi += offIncrement;
406 cTransfers--;
407 }
408#ifdef IN_RC
409 MMGCRamDeregisterTrapHandler(pVM);
410#endif
411 /* Update ecx. */
412 if (pCpu->prefix & PREFIX_REP)
413 pRegFrame->ecx = cTransfers;
414 }
415 else
416 rc = VINF_IOM_HC_MMIO_READ_WRITE;
417 }
418 else
419 {
420 /*
421 * Read operation: [MMIO] -> [mem] or [MMIO] -> [MMIO]
422 * ds:[eSI] (Phys Src) -> es:[eDI] (Virt Dst)
423 */
424 STAM_STATS({ *ppStat = &pVM->iom.s.StatRZInstMovsFromMMIO; });
425
426 /* Check callback. */
427 if (!pRange->CTX_SUFF(pfnReadCallback))
428 return VINF_IOM_HC_MMIO_READ;
429
430 /* Convert destination address. */
431 RTGCUINTPTR pu8Virt;
432 rc = SELMToFlatEx(pVM, DIS_SELREG_ES, pRegFrame, (RTGCPTR)pRegFrame->rdi,
433 SELMTOFLAT_FLAGS_HYPER | SELMTOFLAT_FLAGS_NO_PL,
434 (RTGCPTR *)&pu8Virt);
435 if (RT_FAILURE(rc))
436 return VINF_IOM_HC_MMIO_READ;
437
438 /* Check if destination address is MMIO. */
439 PIOMMMIORANGE pMMIODst;
440 RTGCPHYS PhysDst;
441 rc = PGMGstGetPage(pVM, (RTGCPTR)pu8Virt, NULL, &PhysDst);
442 PhysDst |= (RTGCUINTPTR)pu8Virt & PAGE_OFFSET_MASK;
443 if ( RT_SUCCESS(rc)
444 && (pMMIODst = iomMMIOGetRange(&pVM->iom.s, PhysDst)))
445 {
446 /*
447 * Extra: [MMIO] -> [MMIO]
448 */
449 STAM_STATS({ *ppStat = &pVM->iom.s.StatRZInstMovsMMIO; });
450 if (!pMMIODst->CTX_SUFF(pfnWriteCallback) && pMMIODst->pfnWriteCallbackR3)
451 return VINF_IOM_HC_MMIO_READ_WRITE;
452
453 /* copy loop. */
454 while (cTransfers)
455 {
456 uint32_t u32Data;
457 rc = iomMMIODoRead(pVM, pRange, Phys, &u32Data, cb);
458 if (rc != VINF_SUCCESS)
459 break;
460 rc = iomMMIODoWrite(pVM, pMMIODst, PhysDst, &u32Data, cb);
461 if (rc != VINF_SUCCESS)
462 break;
463
464 Phys += offIncrement;
465 PhysDst += offIncrement;
466 pRegFrame->rsi += offIncrement;
467 pRegFrame->rdi += offIncrement;
468 cTransfers--;
469 }
470 }
471 else
472 {
473 /*
474 * Normal: [MMIO] -> [Mem]
475 */
476 /* Access verification first; we currently can't recover properly from traps inside this instruction */
477 rc = PGMVerifyAccess(pVM, pu8Virt, cTransfers * cb, X86_PTE_RW | ((cpl == 3) ? X86_PTE_US : 0));
478 if (rc != VINF_SUCCESS)
479 {
480 Log(("MOVS will generate a trap -> recompiler, rc=%d\n", rc));
481 return VINF_EM_RAW_EMULATE_INSTR;
482 }
483
484 /* copy loop. */
485#ifdef IN_RC
486 MMGCRamRegisterTrapHandler(pVM);
487#endif
488 while (cTransfers)
489 {
490 uint32_t u32Data;
491 rc = iomMMIODoRead(pVM, pRange, Phys, &u32Data, cb);
492 if (rc != VINF_SUCCESS)
493 break;
494 rc = iomRamWrite(pVM, (RTGCPTR)pu8Virt, &u32Data, cb);
495 if (rc != VINF_SUCCESS)
496 {
497 Log(("iomRamWrite %08X size=%d failed with %d\n", pu8Virt, cb, rc));
498 break;
499 }
500
501 pu8Virt += offIncrement;
502 Phys += offIncrement;
503 pRegFrame->rsi += offIncrement;
504 pRegFrame->rdi += offIncrement;
505 cTransfers--;
506 }
507#ifdef IN_RC
508 MMGCRamDeregisterTrapHandler(pVM);
509#endif
510 }
511
512 /* Update ecx on exit. */
513 if (pCpu->prefix & PREFIX_REP)
514 pRegFrame->ecx = cTransfers;
515 }
516
517 /* work statistics. */
518 if (rc == VINF_SUCCESS)
519 iomMMIOStatLength(pVM, cb);
520 NOREF(ppStat);
521 return rc;
522}
523#endif /* IOM_WITH_MOVS_SUPPORT */
524
525
526/**
527 * [REP] STOSB
528 * [REP] STOSW
529 * [REP] STOSD
530 *
531 * Restricted implementation.
532 *
533 *
534 * @returns VBox status code.
535 *
536 * @param pVM The virtual machine.
537 * @param pRegFrame Trap register frame.
538 * @param GCPhysFault The GC physical address corresponding to pvFault.
539 * @param pCpu Disassembler CPU state.
540 * @param pRange Pointer MMIO range.
541 */
542static int iomInterpretSTOS(PVM pVM, PCPUMCTXCORE pRegFrame, RTGCPHYS GCPhysFault, PDISCPUSTATE pCpu, PIOMMMIORANGE pRange)
543{
544 /*
545 * We do not support segment prefixes or REPNE..
546 */
547 if (pCpu->prefix & (PREFIX_SEG | PREFIX_REPNE))
548 return VINF_IOM_HC_MMIO_READ_WRITE; /** @todo -> REM instead of HC */
549
550 /*
551 * Get bytes/words/dwords count to copy.
552 */
553 uint32_t cTransfers = 1;
554 if (pCpu->prefix & PREFIX_REP)
555 {
556#ifndef IN_RC
557 if ( CPUMIsGuestIn64BitCode(pVM, pRegFrame)
558 && pRegFrame->rcx >= _4G)
559 return VINF_EM_RAW_EMULATE_INSTR;
560#endif
561
562 cTransfers = pRegFrame->ecx;
563 if (SELMGetCpuModeFromSelector(pVM, pRegFrame->eflags, pRegFrame->cs, &pRegFrame->csHid) == CPUMODE_16BIT)
564 cTransfers &= 0xffff;
565
566 if (!cTransfers)
567 return VINF_SUCCESS;
568 }
569
570/** @todo r=bird: bounds checks! */
571
572 /*
573 * Get data size.
574 */
575 unsigned cb = DISGetParamSize(pCpu, &pCpu->param1);
576 AssertMsg(cb > 0 && cb <= sizeof(uint64_t), ("cb=%d\n", cb));
577 int offIncrement = pRegFrame->eflags.Bits.u1DF ? -(signed)cb : (signed)cb;
578
579#ifdef VBOX_WITH_STATISTICS
580 if (pVM->iom.s.cStosMaxBytes < (cTransfers << SIZE_2_SHIFT(cb)))
581 pVM->iom.s.cStosMaxBytes = cTransfers << SIZE_2_SHIFT(cb);
582#endif
583
584
585 RTGCPHYS Phys = GCPhysFault;
586 uint32_t u32Data = pRegFrame->eax;
587 int rc;
588 if (pRange->CTX_SUFF(pfnFillCallback))
589 {
590 /*
591 * Use the fill callback.
592 */
593 /** @todo pfnFillCallback must return number of bytes successfully written!!! */
594 if (offIncrement > 0)
595 {
596 /* addr++ variant. */
597 rc = pRange->CTX_SUFF(pfnFillCallback)(pRange->CTX_SUFF(pDevIns), pRange->CTX_SUFF(pvUser), Phys, u32Data, cb, cTransfers);
598 if (rc == VINF_SUCCESS)
599 {
600 /* Update registers. */
601 pRegFrame->rdi += cTransfers << SIZE_2_SHIFT(cb);
602 if (pCpu->prefix & PREFIX_REP)
603 pRegFrame->ecx = 0;
604 }
605 }
606 else
607 {
608 /* addr-- variant. */
609 rc = pRange->CTX_SUFF(pfnFillCallback)(pRange->CTX_SUFF(pDevIns), pRange->CTX_SUFF(pvUser), (Phys - (cTransfers - 1)) << SIZE_2_SHIFT(cb), u32Data, cb, cTransfers);
610 if (rc == VINF_SUCCESS)
611 {
612 /* Update registers. */
613 pRegFrame->rdi -= cTransfers << SIZE_2_SHIFT(cb);
614 if (pCpu->prefix & PREFIX_REP)
615 pRegFrame->ecx = 0;
616 }
617 }
618 }
619 else
620 {
621 /*
622 * Use the write callback.
623 */
624 Assert(pRange->CTX_SUFF(pfnWriteCallback) || !pRange->pfnWriteCallbackR3);
625
626 /* fill loop. */
627 do
628 {
629 rc = iomMMIODoWrite(pVM, pRange, Phys, &u32Data, cb);
630 if (rc != VINF_SUCCESS)
631 break;
632
633 Phys += offIncrement;
634 pRegFrame->rdi += offIncrement;
635 cTransfers--;
636 } while (cTransfers);
637
638 /* Update ecx on exit. */
639 if (pCpu->prefix & PREFIX_REP)
640 pRegFrame->ecx = cTransfers;
641 }
642
643 /*
644 * Work statistics and return.
645 */
646 if (rc == VINF_SUCCESS)
647 iomMMIOStatLength(pVM, cb);
648 return rc;
649}
650
651
652/**
653 * [REP] LODSB
654 * [REP] LODSW
655 * [REP] LODSD
656 *
657 * Restricted implementation.
658 *
659 *
660 * @returns VBox status code.
661 *
662 * @param pVM The virtual machine.
663 * @param pRegFrame Trap register frame.
664 * @param GCPhysFault The GC physical address corresponding to pvFault.
665 * @param pCpu Disassembler CPU state.
666 * @param pRange Pointer MMIO range.
667 */
668static int iomInterpretLODS(PVM pVM, PCPUMCTXCORE pRegFrame, RTGCPHYS GCPhysFault, PDISCPUSTATE pCpu, PIOMMMIORANGE pRange)
669{
670 Assert(pRange->CTX_SUFF(pfnReadCallback) || !pRange->pfnReadCallbackR3);
671
672 /*
673 * We do not support segment prefixes or REP*.
674 */
675 if (pCpu->prefix & (PREFIX_SEG | PREFIX_REP | PREFIX_REPNE))
676 return VINF_IOM_HC_MMIO_READ_WRITE; /** @todo -> REM instead of HC */
677
678 /*
679 * Get data size.
680 */
681 unsigned cb = DISGetParamSize(pCpu, &pCpu->param2);
682 AssertMsg(cb > 0 && cb <= sizeof(uint64_t), ("cb=%d\n", cb));
683 int offIncrement = pRegFrame->eflags.Bits.u1DF ? -(signed)cb : (signed)cb;
684
685 /*
686 * Perform read.
687 */
688 int rc = iomMMIODoRead(pVM, pRange, GCPhysFault, &pRegFrame->rax, cb);
689 if (rc == VINF_SUCCESS)
690 pRegFrame->rsi += offIncrement;
691
692 /*
693 * Work statistics and return.
694 */
695 if (rc == VINF_SUCCESS)
696 iomMMIOStatLength(pVM, cb);
697 return rc;
698}
699
700
701/**
702 * CMP [MMIO], reg|imm
703 * CMP reg|imm, [MMIO]
704 *
705 * Restricted implementation.
706 *
707 *
708 * @returns VBox status code.
709 *
710 * @param pVM The virtual machine.
711 * @param pRegFrame Trap register frame.
712 * @param GCPhysFault The GC physical address corresponding to pvFault.
713 * @param pCpu Disassembler CPU state.
714 * @param pRange Pointer MMIO range.
715 */
716static int iomInterpretCMP(PVM pVM, PCPUMCTXCORE pRegFrame, RTGCPHYS GCPhysFault, PDISCPUSTATE pCpu, PIOMMMIORANGE pRange)
717{
718 Assert(pRange->CTX_SUFF(pfnReadCallback) || !pRange->pfnReadCallbackR3);
719
720 /*
721 * Get the operands.
722 */
723 unsigned cb = 0;
724 uint64_t uData1 = 0;
725 uint64_t uData2 = 0;
726 int rc;
727 if (iomGetRegImmData(pCpu, &pCpu->param1, pRegFrame, &uData1, &cb))
728 /* cmp reg, [MMIO]. */
729 rc = iomMMIODoRead(pVM, pRange, GCPhysFault, &uData2, cb);
730 else if (iomGetRegImmData(pCpu, &pCpu->param2, pRegFrame, &uData2, &cb))
731 /* cmp [MMIO], reg|imm. */
732 rc = iomMMIODoRead(pVM, pRange, GCPhysFault, &uData1, cb);
733 else
734 {
735 AssertMsgFailed(("Disassember CMP problem..\n"));
736 rc = VERR_IOM_MMIO_HANDLER_DISASM_ERROR;
737 }
738
739 if (rc == VINF_SUCCESS)
740 {
741 /* Emulate CMP and update guest flags. */
742 uint32_t eflags = EMEmulateCmp(uData1, uData2, cb);
743 pRegFrame->eflags.u32 = (pRegFrame->eflags.u32 & ~(X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF | X86_EFL_OF))
744 | (eflags & (X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF | X86_EFL_OF));
745 iomMMIOStatLength(pVM, cb);
746 }
747
748 return rc;
749}
750
751
752/**
753 * AND [MMIO], reg|imm
754 * AND reg, [MMIO]
755 * OR [MMIO], reg|imm
756 * OR reg, [MMIO]
757 *
758 * Restricted implementation.
759 *
760 *
761 * @returns VBox status code.
762 *
763 * @param pVM The virtual machine.
764 * @param pRegFrame Trap register frame.
765 * @param GCPhysFault The GC physical address corresponding to pvFault.
766 * @param pCpu Disassembler CPU state.
767 * @param pRange Pointer MMIO range.
768 * @param pfnEmulate Instruction emulation function.
769 */
770static int iomInterpretOrXorAnd(PVM pVM, PCPUMCTXCORE pRegFrame, RTGCPHYS GCPhysFault, PDISCPUSTATE pCpu, PIOMMMIORANGE pRange, PFNEMULATEPARAM3 pfnEmulate)
771{
772 unsigned cb = 0;
773 uint64_t uData1 = 0;
774 uint64_t uData2 = 0;
775 bool fAndWrite;
776 int rc;
777
778#ifdef LOG_ENABLED
779 const char *pszInstr;
780
781 if (pCpu->pCurInstr->opcode == OP_XOR)
782 pszInstr = "Xor";
783 else if (pCpu->pCurInstr->opcode == OP_OR)
784 pszInstr = "Or";
785 else if (pCpu->pCurInstr->opcode == OP_AND)
786 pszInstr = "And";
787 else
788 pszInstr = "OrXorAnd??";
789#endif
790
791 if (iomGetRegImmData(pCpu, &pCpu->param1, pRegFrame, &uData1, &cb))
792 {
793 /* and reg, [MMIO]. */
794 Assert(pRange->CTX_SUFF(pfnReadCallback) || !pRange->pfnReadCallbackR3);
795 fAndWrite = false;
796 rc = iomMMIODoRead(pVM, pRange, GCPhysFault, &uData2, cb);
797 }
798 else if (iomGetRegImmData(pCpu, &pCpu->param2, pRegFrame, &uData2, &cb))
799 {
800 /* and [MMIO], reg|imm. */
801 fAndWrite = true;
802 if ( (pRange->CTX_SUFF(pfnReadCallback) || !pRange->pfnReadCallbackR3)
803 && (pRange->CTX_SUFF(pfnWriteCallback) || !pRange->pfnWriteCallbackR3))
804 rc = iomMMIODoRead(pVM, pRange, GCPhysFault, &uData1, cb);
805 else
806 rc = VINF_IOM_HC_MMIO_READ_WRITE;
807 }
808 else
809 {
810 AssertMsgFailed(("Disassember AND problem..\n"));
811 return VERR_IOM_MMIO_HANDLER_DISASM_ERROR;
812 }
813
814 if (rc == VINF_SUCCESS)
815 {
816 /* Emulate AND and update guest flags. */
817 uint32_t eflags = pfnEmulate((uint32_t *)&uData1, uData2, cb);
818
819 LogFlow(("iomInterpretOrXorAnd %s result %RX64\n", pszInstr, uData1));
820
821 if (fAndWrite)
822 /* Store result to MMIO. */
823 rc = iomMMIODoWrite(pVM, pRange, GCPhysFault, &uData1, cb);
824 else
825 {
826 /* Store result to register. */
827 bool fRc = iomSaveDataToReg(pCpu, &pCpu->param1, pRegFrame, uData1);
828 AssertMsg(fRc, ("Failed to store register value!\n")); NOREF(fRc);
829 }
830 if (rc == VINF_SUCCESS)
831 {
832 /* Update guest's eflags and finish. */
833 pRegFrame->eflags.u32 = (pRegFrame->eflags.u32 & ~(X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF | X86_EFL_OF))
834 | (eflags & (X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF | X86_EFL_OF));
835 iomMMIOStatLength(pVM, cb);
836 }
837 }
838
839 return rc;
840}
841
842
843/**
844 * TEST [MMIO], reg|imm
845 * TEST reg, [MMIO]
846 *
847 * Restricted implementation.
848 *
849 *
850 * @returns VBox status code.
851 *
852 * @param pVM The virtual machine.
853 * @param pRegFrame Trap register frame.
854 * @param GCPhysFault The GC physical address corresponding to pvFault.
855 * @param pCpu Disassembler CPU state.
856 * @param pRange Pointer MMIO range.
857 */
858static int iomInterpretTEST(PVM pVM, PCPUMCTXCORE pRegFrame, RTGCPHYS GCPhysFault, PDISCPUSTATE pCpu, PIOMMMIORANGE pRange)
859{
860 Assert(pRange->CTX_SUFF(pfnReadCallback) || !pRange->pfnReadCallbackR3);
861
862 unsigned cb = 0;
863 uint64_t uData1 = 0;
864 uint64_t uData2 = 0;
865 int rc;
866
867 if (iomGetRegImmData(pCpu, &pCpu->param1, pRegFrame, &uData1, &cb))
868 {
869 /* and test, [MMIO]. */
870 rc = iomMMIODoRead(pVM, pRange, GCPhysFault, &uData2, cb);
871 }
872 else if (iomGetRegImmData(pCpu, &pCpu->param2, pRegFrame, &uData2, &cb))
873 {
874 /* test [MMIO], reg|imm. */
875 rc = iomMMIODoRead(pVM, pRange, GCPhysFault, &uData1, cb);
876 }
877 else
878 {
879 AssertMsgFailed(("Disassember TEST problem..\n"));
880 return VERR_IOM_MMIO_HANDLER_DISASM_ERROR;
881 }
882
883 if (rc == VINF_SUCCESS)
884 {
885 /* Emulate TEST (=AND without write back) and update guest EFLAGS. */
886 uint32_t eflags = EMEmulateAnd((uint32_t *)&uData1, uData2, cb);
887 pRegFrame->eflags.u32 = (pRegFrame->eflags.u32 & ~(X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF | X86_EFL_OF))
888 | (eflags & (X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF | X86_EFL_OF));
889 iomMMIOStatLength(pVM, cb);
890 }
891
892 return rc;
893}
894
895
896/**
897 * BT [MMIO], reg|imm
898 *
899 * Restricted implementation.
900 *
901 *
902 * @returns VBox status code.
903 *
904 * @param pVM The virtual machine.
905 * @param pRegFrame Trap register frame.
906 * @param GCPhysFault The GC physical address corresponding to pvFault.
907 * @param pCpu Disassembler CPU state.
908 * @param pRange Pointer MMIO range.
909 */
910static int iomInterpretBT(PVM pVM, PCPUMCTXCORE pRegFrame, RTGCPHYS GCPhysFault, PDISCPUSTATE pCpu, PIOMMMIORANGE pRange)
911{
912 Assert(pRange->CTX_SUFF(pfnReadCallback) || !pRange->pfnReadCallbackR3);
913
914 uint64_t uBit = 0;
915 uint64_t uData1 = 0;
916 unsigned cb = 0;
917 int rc;
918
919 if (iomGetRegImmData(pCpu, &pCpu->param2, pRegFrame, &uBit, &cb))
920 {
921 /* bt [MMIO], reg|imm. */
922 rc = iomMMIODoRead(pVM, pRange, GCPhysFault, &uData1, cb);
923 }
924 else
925 {
926 AssertMsgFailed(("Disassember BT problem..\n"));
927 return VERR_IOM_MMIO_HANDLER_DISASM_ERROR;
928 }
929
930 if (rc == VINF_SUCCESS)
931 {
932 /* The size of the memory operand only matters here. */
933 cb = DISGetParamSize(pCpu, &pCpu->param1);
934
935 /* Find the bit inside the faulting address */
936 uBit &= (cb*8 - 1);
937
938 pRegFrame->eflags.Bits.u1CF = (uData1 >> uBit);
939 iomMMIOStatLength(pVM, cb);
940 }
941
942 return rc;
943}
944
945/**
946 * XCHG [MMIO], reg
947 * XCHG reg, [MMIO]
948 *
949 * Restricted implementation.
950 *
951 *
952 * @returns VBox status code.
953 *
954 * @param pVM The virtual machine.
955 * @param pRegFrame Trap register frame.
956 * @param GCPhysFault The GC physical address corresponding to pvFault.
957 * @param pCpu Disassembler CPU state.
958 * @param pRange Pointer MMIO range.
959 */
960static int iomInterpretXCHG(PVM pVM, PCPUMCTXCORE pRegFrame, RTGCPHYS GCPhysFault, PDISCPUSTATE pCpu, PIOMMMIORANGE pRange)
961{
962 /* Check for read & write handlers since IOMMMIOHandler doesn't cover this. */
963 if ( (!pRange->CTX_SUFF(pfnReadCallback) && pRange->pfnReadCallbackR3)
964 || (!pRange->CTX_SUFF(pfnWriteCallback) && pRange->pfnWriteCallbackR3))
965 return VINF_IOM_HC_MMIO_READ_WRITE;
966
967 int rc;
968 unsigned cb = 0;
969 uint64_t uData1 = 0;
970 uint64_t uData2 = 0;
971 if (iomGetRegImmData(pCpu, &pCpu->param1, pRegFrame, &uData1, &cb))
972 {
973 /* xchg reg, [MMIO]. */
974 rc = iomMMIODoRead(pVM, pRange, GCPhysFault, &uData2, cb);
975 if (rc == VINF_SUCCESS)
976 {
977 /* Store result to MMIO. */
978 rc = iomMMIODoWrite(pVM, pRange, GCPhysFault, &uData1, cb);
979
980 if (rc == VINF_SUCCESS)
981 {
982 /* Store result to register. */
983 bool fRc = iomSaveDataToReg(pCpu, &pCpu->param1, pRegFrame, uData2);
984 AssertMsg(fRc, ("Failed to store register value!\n")); NOREF(fRc);
985 }
986 else
987 Assert(rc == VINF_IOM_HC_MMIO_WRITE || rc == VINF_PATM_HC_MMIO_PATCH_WRITE);
988 }
989 else
990 Assert(rc == VINF_IOM_HC_MMIO_READ || rc == VINF_PATM_HC_MMIO_PATCH_READ);
991 }
992 else if (iomGetRegImmData(pCpu, &pCpu->param2, pRegFrame, &uData2, &cb))
993 {
994 /* xchg [MMIO], reg. */
995 rc = iomMMIODoRead(pVM, pRange, GCPhysFault, &uData1, cb);
996 if (rc == VINF_SUCCESS)
997 {
998 /* Store result to MMIO. */
999 rc = iomMMIODoWrite(pVM, pRange, GCPhysFault, &uData2, cb);
1000 if (rc == VINF_SUCCESS)
1001 {
1002 /* Store result to register. */
1003 bool fRc = iomSaveDataToReg(pCpu, &pCpu->param2, pRegFrame, uData1);
1004 AssertMsg(fRc, ("Failed to store register value!\n")); NOREF(fRc);
1005 }
1006 else
1007 Assert(rc == VINF_IOM_HC_MMIO_WRITE || rc == VINF_PATM_HC_MMIO_PATCH_WRITE);
1008 }
1009 else
1010 Assert(rc == VINF_IOM_HC_MMIO_READ || rc == VINF_PATM_HC_MMIO_PATCH_READ);
1011 }
1012 else
1013 {
1014 AssertMsgFailed(("Disassember XCHG problem..\n"));
1015 rc = VERR_IOM_MMIO_HANDLER_DISASM_ERROR;
1016 }
1017 return rc;
1018}
1019
1020
1021/**
1022 * \#PF Handler callback for MMIO ranges.
1023 *
1024 * @returns VBox status code (appropriate for GC return).
1025 * @param pVM VM Handle.
1026 * @param uErrorCode CPU Error code.
1027 * @param pCtxCore Trap register frame.
1028 * @param pvFault The fault address (cr2).
1029 * @param GCPhysFault The GC physical address corresponding to pvFault.
1030 * @param pvUser Pointer to the MMIO ring-3 range entry.
1031 */
1032VMMDECL(int) IOMMMIOHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pCtxCore, RTGCPTR pvFault, RTGCPHYS GCPhysFault, void *pvUser)
1033{
1034 STAM_PROFILE_START(&pVM->iom.s.StatRZMMIOHandler, a);
1035 Log(("IOMMMIOHandler: GCPhys=%RGp uErr=%#x pvFault=%RGv rip=%RGv\n",
1036 GCPhysFault, (uint32_t)uErrorCode, pvFault, (RTGCPTR)pCtxCore->rip));
1037
1038 PIOMMMIORANGE pRange = (PIOMMMIORANGE)pvUser;
1039 Assert(pRange);
1040 Assert(pRange == iomMMIOGetRange(&pVM->iom.s, GCPhysFault));
1041
1042#ifdef VBOX_WITH_STATISTICS
1043 /*
1044 * Locate the statistics, if > PAGE_SIZE we'll use the first byte for everything.
1045 */
1046 PIOMMMIOSTATS pStats = iomMMIOGetStats(&pVM->iom.s, GCPhysFault, pRange);
1047 if (!pStats)
1048 {
1049# ifdef IN_RING3
1050 return VERR_NO_MEMORY;
1051# else
1052 STAM_PROFILE_STOP(&pVM->iom.s.StatRZMMIOHandler, a);
1053 STAM_COUNTER_INC(&pVM->iom.s.StatRZMMIOFailures);
1054 return uErrorCode & X86_TRAP_PF_RW ? VINF_IOM_HC_MMIO_WRITE : VINF_IOM_HC_MMIO_READ;
1055# endif
1056 }
1057#endif
1058
1059#ifndef IN_RING3
1060 /*
1061 * Should we defer the request right away?
1062 */
1063 if (uErrorCode & X86_TRAP_PF_RW
1064 ? !pRange->CTX_SUFF(pfnWriteCallback) && pRange->pfnWriteCallbackR3
1065 : !pRange->CTX_SUFF(pfnReadCallback) && pRange->pfnReadCallbackR3)
1066 {
1067# ifdef VBOX_WITH_STATISTICS
1068 if (uErrorCode & X86_TRAP_PF_RW)
1069 STAM_COUNTER_INC(&pStats->CTX_MID_Z(Write,ToR3));
1070 else
1071 STAM_COUNTER_INC(&pStats->CTX_MID_Z(Read,ToR3));
1072# endif
1073
1074 STAM_PROFILE_STOP(&pVM->iom.s.StatRZMMIOHandler, a);
1075 STAM_COUNTER_INC(&pVM->iom.s.StatRZMMIOFailures);
1076 return uErrorCode & X86_TRAP_PF_RW ? VINF_IOM_HC_MMIO_WRITE : VINF_IOM_HC_MMIO_READ;
1077 }
1078#endif /* !IN_RING3 */
1079
1080 /*
1081 * Disassemble the instruction and interpret it.
1082 */
1083 DISCPUSTATE Cpu;
1084 unsigned cbOp;
1085 int rc = EMInterpretDisasOne(pVM, pCtxCore, &Cpu, &cbOp);
1086 AssertRCReturn(rc, rc);
1087 switch (Cpu.pCurInstr->opcode)
1088 {
1089 case OP_MOV:
1090 case OP_MOVZX:
1091 case OP_MOVSX:
1092 {
1093 STAM_PROFILE_START(&pVM->iom.s.StatRZInstMov, b);
1094 if (uErrorCode & X86_TRAP_PF_RW)
1095 rc = iomInterpretMOVxXWrite(pVM, pCtxCore, &Cpu, pRange, GCPhysFault);
1096 else
1097 rc = iomInterpretMOVxXRead(pVM, pCtxCore, &Cpu, pRange, GCPhysFault);
1098 STAM_PROFILE_STOP(&pVM->iom.s.StatRZInstMov, b);
1099 break;
1100 }
1101
1102
1103#ifdef IOM_WITH_MOVS_SUPPORT
1104 case OP_MOVSB:
1105 case OP_MOVSWD:
1106 {
1107 STAM_PROFILE_ADV_START(&pVM->iom.s.StatRZInstMovs, c);
1108 PSTAMPROFILE pStat = NULL;
1109 rc = iomInterpretMOVS(pVM, uErrorCode, pCtxCore, GCPhysFault, &Cpu, pRange, &pStat);
1110 STAM_PROFILE_ADV_STOP_EX(&pVM->iom.s.StatRZInstMovs, pStat, c);
1111 break;
1112 }
1113#endif
1114
1115 case OP_STOSB:
1116 case OP_STOSWD:
1117 Assert(uErrorCode & X86_TRAP_PF_RW);
1118 STAM_PROFILE_START(&pVM->iom.s.StatRZInstStos, d);
1119 rc = iomInterpretSTOS(pVM, pCtxCore, GCPhysFault, &Cpu, pRange);
1120 STAM_PROFILE_STOP(&pVM->iom.s.StatRZInstStos, d);
1121 break;
1122
1123 case OP_LODSB:
1124 case OP_LODSWD:
1125 Assert(!(uErrorCode & X86_TRAP_PF_RW));
1126 STAM_PROFILE_START(&pVM->iom.s.StatRZInstLods, e);
1127 rc = iomInterpretLODS(pVM, pCtxCore, GCPhysFault, &Cpu, pRange);
1128 STAM_PROFILE_STOP(&pVM->iom.s.StatRZInstLods, e);
1129 break;
1130
1131 case OP_CMP:
1132 Assert(!(uErrorCode & X86_TRAP_PF_RW));
1133 STAM_PROFILE_START(&pVM->iom.s.StatRZInstCmp, f);
1134 rc = iomInterpretCMP(pVM, pCtxCore, GCPhysFault, &Cpu, pRange);
1135 STAM_PROFILE_STOP(&pVM->iom.s.StatRZInstCmp, f);
1136 break;
1137
1138 case OP_AND:
1139 STAM_PROFILE_START(&pVM->iom.s.StatRZInstAnd, g);
1140 rc = iomInterpretOrXorAnd(pVM, pCtxCore, GCPhysFault, &Cpu, pRange, EMEmulateAnd);
1141 STAM_PROFILE_STOP(&pVM->iom.s.StatRZInstAnd, g);
1142 break;
1143
1144 case OP_OR:
1145 STAM_PROFILE_START(&pVM->iom.s.StatRZInstOr, k);
1146 rc = iomInterpretOrXorAnd(pVM, pCtxCore, GCPhysFault, &Cpu, pRange, EMEmulateOr);
1147 STAM_PROFILE_STOP(&pVM->iom.s.StatRZInstOr, k);
1148 break;
1149
1150 case OP_XOR:
1151 STAM_PROFILE_START(&pVM->iom.s.StatRZInstXor, m);
1152 rc = iomInterpretOrXorAnd(pVM, pCtxCore, GCPhysFault, &Cpu, pRange, EMEmulateXor);
1153 STAM_PROFILE_STOP(&pVM->iom.s.StatRZInstXor, m);
1154 break;
1155
1156 case OP_TEST:
1157 Assert(!(uErrorCode & X86_TRAP_PF_RW));
1158 STAM_PROFILE_START(&pVM->iom.s.StatRZInstTest, h);
1159 rc = iomInterpretTEST(pVM, pCtxCore, GCPhysFault, &Cpu, pRange);
1160 STAM_PROFILE_STOP(&pVM->iom.s.StatRZInstTest, h);
1161 break;
1162
1163 case OP_BT:
1164 Assert(!(uErrorCode & X86_TRAP_PF_RW));
1165 STAM_PROFILE_START(&pVM->iom.s.StatRZInstBt, l);
1166 rc = iomInterpretBT(pVM, pCtxCore, GCPhysFault, &Cpu, pRange);
1167 STAM_PROFILE_STOP(&pVM->iom.s.StatRZInstBt, l);
1168 break;
1169
1170 case OP_XCHG:
1171 STAM_PROFILE_START(&pVM->iom.s.StatRZInstXchg, i);
1172 rc = iomInterpretXCHG(pVM, pCtxCore, GCPhysFault, &Cpu, pRange);
1173 STAM_PROFILE_STOP(&pVM->iom.s.StatRZInstXchg, i);
1174 break;
1175
1176
1177 /*
1178 * The instruction isn't supported. Hand it on to ring-3.
1179 */
1180 default:
1181 STAM_COUNTER_INC(&pVM->iom.s.StatRZInstOther);
1182 rc = (uErrorCode & X86_TRAP_PF_RW) ? VINF_IOM_HC_MMIO_WRITE : VINF_IOM_HC_MMIO_READ;
1183 break;
1184 }
1185
1186 /*
1187 * On success advance EIP.
1188 */
1189 if (rc == VINF_SUCCESS)
1190 pCtxCore->rip += cbOp;
1191 else
1192 {
1193 STAM_COUNTER_INC(&pVM->iom.s.StatRZMMIOFailures);
1194#if defined(VBOX_WITH_STATISTICS) && !defined(IN_RING3)
1195 switch (rc)
1196 {
1197 case VINF_IOM_HC_MMIO_READ:
1198 case VINF_IOM_HC_MMIO_READ_WRITE:
1199 STAM_COUNTER_INC(&pStats->CTX_MID_Z(Read,ToR3));
1200 break;
1201 case VINF_IOM_HC_MMIO_WRITE:
1202 STAM_COUNTER_INC(&pStats->CTX_MID_Z(Write,ToR3));
1203 break;
1204 }
1205#endif
1206 }
1207
1208 STAM_PROFILE_STOP(&pVM->iom.s.StatRZMMIOHandler, a);
1209 return rc;
1210}
1211
1212
1213#ifdef IN_RING3
1214/**
1215 * \#PF Handler callback for MMIO ranges.
1216 *
1217 * @returns VINF_SUCCESS if the handler have carried out the operation.
1218 * @returns VINF_PGM_HANDLER_DO_DEFAULT if the caller should carry out the access operation.
1219 * @param pVM VM Handle.
1220 * @param GCPhys The physical address the guest is writing to.
1221 * @param pvPhys The HC mapping of that address.
1222 * @param pvBuf What the guest is reading/writing.
1223 * @param cbBuf How much it's reading/writing.
1224 * @param enmAccessType The access type.
1225 * @param pvUser Pointer to the MMIO range entry.
1226 */
1227DECLCALLBACK(int) IOMR3MMIOHandler(PVM pVM, RTGCPHYS GCPhysFault, void *pvPhys, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
1228{
1229 int rc;
1230 PIOMMMIORANGE pRange = (PIOMMMIORANGE)pvUser;
1231 STAM_COUNTER_INC(&pVM->iom.s.StatR3MMIOHandler);
1232
1233 AssertMsg(cbBuf == 1 || cbBuf == 2 || cbBuf == 4 || cbBuf == 8, ("%zu\n", cbBuf));
1234
1235 Assert(pRange);
1236 Assert(pRange == iomMMIOGetRange(&pVM->iom.s, GCPhysFault));
1237
1238 if (enmAccessType == PGMACCESSTYPE_READ)
1239 rc = iomMMIODoRead(pVM, pRange, GCPhysFault, pvBuf, (unsigned)cbBuf);
1240 else
1241 rc = iomMMIODoWrite(pVM, pRange, GCPhysFault, pvBuf, (unsigned)cbBuf);
1242
1243 AssertRC(rc);
1244 return rc;
1245}
1246#endif /* IN_RING3 */
1247
1248
1249/**
1250 * Reads a MMIO register.
1251 *
1252 * @returns VBox status code.
1253 *
1254 * @param pVM VM handle.
1255 * @param GCPhys The physical address to read.
1256 * @param pu32Value Where to store the value read.
1257 * @param cbValue The size of the register to read in bytes. 1, 2 or 4 bytes.
1258 */
1259VMMDECL(int) IOMMMIORead(PVM pVM, RTGCPHYS GCPhys, uint32_t *pu32Value, size_t cbValue)
1260{
1261 /*
1262 * Lookup the current context range node and statistics.
1263 */
1264 PIOMMMIORANGE pRange = iomMMIOGetRange(&pVM->iom.s, GCPhys);
1265 AssertMsgReturn(pRange,
1266 ("Handlers and page tables are out of sync or something! GCPhys=%RGp cbValue=%d\n", GCPhys, cbValue),
1267 VERR_INTERNAL_ERROR);
1268#ifdef VBOX_WITH_STATISTICS
1269 PIOMMMIOSTATS pStats = iomMMIOGetStats(&pVM->iom.s, GCPhys, pRange);
1270 if (!pStats)
1271# ifdef IN_RING3
1272 return VERR_NO_MEMORY;
1273# else
1274 return VINF_IOM_HC_MMIO_READ;
1275# endif
1276#endif /* VBOX_WITH_STATISTICS */
1277 if (pRange->CTX_SUFF(pfnReadCallback))
1278 {
1279 /*
1280 * Perform the read and deal with the result.
1281 */
1282#ifdef VBOX_WITH_STATISTICS
1283 STAM_PROFILE_ADV_START(&pStats->CTX_SUFF_Z(ProfRead), a);
1284#endif
1285 int rc = pRange->CTX_SUFF(pfnReadCallback)(pRange->CTX_SUFF(pDevIns), pRange->CTX_SUFF(pvUser), GCPhys, pu32Value, (unsigned)cbValue);
1286#ifdef VBOX_WITH_STATISTICS
1287 STAM_PROFILE_ADV_STOP(&pStats->CTX_SUFF_Z(ProfRead), a);
1288 if (rc != VINF_IOM_HC_MMIO_READ)
1289 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Read));
1290#endif
1291 switch (rc)
1292 {
1293 case VINF_SUCCESS:
1294 default:
1295 Log4(("IOMMMIORead: GCPhys=%RGp *pu32=%08RX32 cb=%d rc=%Rrc\n", GCPhys, *pu32Value, cbValue, rc));
1296 return rc;
1297
1298 case VINF_IOM_MMIO_UNUSED_00:
1299 switch (cbValue)
1300 {
1301 case 1: *(uint8_t *)pu32Value = UINT8_C(0x00); break;
1302 case 2: *(uint16_t *)pu32Value = UINT16_C(0x0000); break;
1303 case 4: *(uint32_t *)pu32Value = UINT32_C(0x00000000); break;
1304 case 8: *(uint64_t *)pu32Value = UINT64_C(0x0000000000000000); break;
1305 default: AssertReleaseMsgFailed(("cbValue=%d GCPhys=%RGp\n", cbValue, GCPhys)); break;
1306 }
1307 Log4(("IOMMMIORead: GCPhys=%RGp *pu32=%08RX32 cb=%d rc=%Rrc\n", GCPhys, *pu32Value, cbValue, rc));
1308 return VINF_SUCCESS;
1309
1310 case VINF_IOM_MMIO_UNUSED_FF:
1311 switch (cbValue)
1312 {
1313 case 1: *(uint8_t *)pu32Value = UINT8_C(0xff); break;
1314 case 2: *(uint16_t *)pu32Value = UINT16_C(0xffff); break;
1315 case 4: *(uint32_t *)pu32Value = UINT32_C(0xffffffff); break;
1316 case 8: *(uint64_t *)pu32Value = UINT64_C(0xffffffffffffffff); break;
1317 default: AssertReleaseMsgFailed(("cbValue=%d GCPhys=%RGp\n", cbValue, GCPhys)); break;
1318 }
1319 Log4(("IOMMMIORead: GCPhys=%RGp *pu32=%08RX32 cb=%d rc=%Rrc\n", GCPhys, *pu32Value, cbValue, rc));
1320 return VINF_SUCCESS;
1321 }
1322 }
1323#ifndef IN_RING3
1324 if (pRange->pfnReadCallbackR3)
1325 {
1326 STAM_COUNTER_INC(&pStats->CTX_MID_Z(Read,ToR3));
1327 return VINF_IOM_HC_MMIO_READ;
1328 }
1329#endif
1330
1331 /*
1332 * Lookup the ring-3 range.
1333 */
1334#ifdef VBOX_WITH_STATISTICS
1335 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Read));
1336#endif
1337 /* Unassigned memory; this is actually not supposed to happen. */
1338 switch (cbValue)
1339 {
1340 case 1: *(uint8_t *)pu32Value = UINT8_C(0xff); break;
1341 case 2: *(uint16_t *)pu32Value = UINT16_C(0xffff); break;
1342 case 4: *(uint32_t *)pu32Value = UINT32_C(0xffffffff); break;
1343 case 8: *(uint64_t *)pu32Value = UINT64_C(0xffffffffffffffff); break;
1344 default: AssertReleaseMsgFailed(("cbValue=%d GCPhys=%RGp\n", cbValue, GCPhys)); break;
1345 }
1346 Log4(("IOMMMIORead: GCPhys=%RGp *pu32=%08RX32 cb=%d rc=VINF_SUCCESS\n", GCPhys, *pu32Value, cbValue));
1347 return VINF_SUCCESS;
1348}
1349
1350
1351/**
1352 * Writes to a MMIO register.
1353 *
1354 * @returns VBox status code.
1355 *
1356 * @param pVM VM handle.
1357 * @param GCPhys The physical address to write to.
1358 * @param u32Value The value to write.
1359 * @param cbValue The size of the register to read in bytes. 1, 2 or 4 bytes.
1360 */
1361VMMDECL(int) IOMMMIOWrite(PVM pVM, RTGCPHYS GCPhys, uint32_t u32Value, size_t cbValue)
1362{
1363 /*
1364 * Lookup the current context range node.
1365 */
1366 PIOMMMIORANGE pRange = iomMMIOGetRange(&pVM->iom.s, GCPhys);
1367 AssertMsgReturn(pRange,
1368 ("Handlers and page tables are out of sync or something! GCPhys=%RGp cbValue=%d\n", GCPhys, cbValue),
1369 VERR_INTERNAL_ERROR);
1370#ifdef VBOX_WITH_STATISTICS
1371 PIOMMMIOSTATS pStats = iomMMIOGetStats(&pVM->iom.s, GCPhys, pRange);
1372 if (!pStats)
1373# ifdef IN_RING3
1374 return VERR_NO_MEMORY;
1375# else
1376 return VINF_IOM_HC_MMIO_WRITE;
1377# endif
1378#endif /* VBOX_WITH_STATISTICS */
1379
1380 /*
1381 * Perform the write if there's a write handler. R0/GC may have
1382 * to defer it to ring-3.
1383 */
1384 if (pRange->CTX_SUFF(pfnWriteCallback))
1385 {
1386#ifdef VBOX_WITH_STATISTICS
1387 STAM_PROFILE_ADV_START(&pStats->CTX_SUFF_Z(ProfWrite), a);
1388#endif
1389 int rc = pRange->CTX_SUFF(pfnWriteCallback)(pRange->CTX_SUFF(pDevIns), pRange->CTX_SUFF(pvUser), GCPhys, &u32Value, (unsigned)cbValue);
1390#ifdef VBOX_WITH_STATISTICS
1391 STAM_PROFILE_ADV_STOP(&pStats->CTX_SUFF_Z(ProfWrite), a);
1392 if (rc != VINF_IOM_HC_MMIO_WRITE)
1393 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Write));
1394#endif
1395 Log4(("IOMMMIOWrite: GCPhys=%RGp u32=%08RX32 cb=%d rc=%Rrc\n", GCPhys, u32Value, cbValue, rc));
1396 return rc;
1397 }
1398#ifndef IN_RING3
1399 if (pRange->pfnWriteCallbackR3)
1400 {
1401 STAM_COUNTER_INC(&pStats->CTX_MID_Z(Write,ToR3));
1402 return VINF_IOM_HC_MMIO_WRITE;
1403 }
1404#endif
1405
1406 /*
1407 * No write handler, nothing to do.
1408 */
1409#ifdef VBOX_WITH_STATISTICS
1410 STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Write));
1411#endif
1412 Log4(("IOMMMIOWrite: GCPhys=%RGp u32=%08RX32 cb=%d rc=%Rrc\n", GCPhys, u32Value, cbValue, VINF_SUCCESS));
1413 return VINF_SUCCESS;
1414}
1415
1416
1417/**
1418 * [REP*] INSB/INSW/INSD
1419 * ES:EDI,DX[,ECX]
1420 *
1421 * @remark Assumes caller checked the access privileges (IOMInterpretCheckPortIOAccess)
1422 *
1423 * @returns Strict VBox status code. Informational status codes other than the one documented
1424 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
1425 * @retval VINF_SUCCESS Success.
1426 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
1427 * status code must be passed on to EM.
1428 * @retval VINF_IOM_HC_IOPORT_READ Defer the read to ring-3. (R0/GC only)
1429 * @retval VINF_EM_RAW_EMULATE_INSTR Defer the read to the REM.
1430 * @retval VINF_EM_RAW_GUEST_TRAP The exception was left pending. (TRPMRaiseXcptErr)
1431 * @retval VINF_TRPM_XCPT_DISPATCHED The exception was raised and dispatched for raw-mode execution. (TRPMRaiseXcptErr)
1432 * @retval VINF_EM_RESCHEDULE_REM The exception was dispatched and cannot be executed in raw-mode. (TRPMRaiseXcptErr)
1433 *
1434 * @param pVM The virtual machine.
1435 * @param pRegFrame Pointer to CPUMCTXCORE guest registers structure.
1436 * @param uPort IO Port
1437 * @param uPrefix IO instruction prefix
1438 * @param cbTransfer Size of transfer unit
1439 */
1440VMMDECL(int) IOMInterpretINSEx(PVM pVM, PCPUMCTXCORE pRegFrame, uint32_t uPort, uint32_t uPrefix, uint32_t cbTransfer)
1441{
1442#ifdef VBOX_WITH_STATISTICS
1443 STAM_COUNTER_INC(&pVM->iom.s.StatInstIns);
1444#endif
1445
1446 /*
1447 * We do not support REPNE or decrementing destination
1448 * pointer. Segment prefixes are deliberately ignored, as per the instruction specification.
1449 */
1450 if ( (uPrefix & PREFIX_REPNE)
1451 || pRegFrame->eflags.Bits.u1DF)
1452 return VINF_EM_RAW_EMULATE_INSTR;
1453
1454 /*
1455 * Get bytes/words/dwords count to transfer.
1456 */
1457 RTGCUINTREG cTransfers = 1;
1458 if (uPrefix & PREFIX_REP)
1459 {
1460#ifndef IN_RC
1461 if ( CPUMIsGuestIn64BitCode(pVM, pRegFrame)
1462 && pRegFrame->rcx >= _4G)
1463 return VINF_EM_RAW_EMULATE_INSTR;
1464#endif
1465 cTransfers = pRegFrame->ecx;
1466
1467 if (SELMGetCpuModeFromSelector(pVM, pRegFrame->eflags, pRegFrame->cs, &pRegFrame->csHid) == CPUMODE_16BIT)
1468 cTransfers &= 0xffff;
1469
1470 if (!cTransfers)
1471 return VINF_SUCCESS;
1472 }
1473
1474 /* Convert destination address es:edi. */
1475 RTGCPTR GCPtrDst;
1476 int rc = SELMToFlatEx(pVM, DIS_SELREG_ES, pRegFrame, (RTGCPTR)pRegFrame->rdi,
1477 SELMTOFLAT_FLAGS_HYPER | SELMTOFLAT_FLAGS_NO_PL,
1478 &GCPtrDst);
1479 if (RT_FAILURE(rc))
1480 {
1481 Log(("INS destination address conversion failed -> fallback, rc=%d\n", rc));
1482 return VINF_EM_RAW_EMULATE_INSTR;
1483 }
1484
1485 /* Access verification first; we can't recover from traps inside this instruction, as the port read cannot be repeated. */
1486 uint32_t cpl = CPUMGetGuestCPL(pVM, pRegFrame);
1487
1488 rc = PGMVerifyAccess(pVM, (RTGCUINTPTR)GCPtrDst, cTransfers * cbTransfer,
1489 X86_PTE_RW | ((cpl == 3) ? X86_PTE_US : 0));
1490 if (rc != VINF_SUCCESS)
1491 {
1492 Log(("INS will generate a trap -> fallback, rc=%d\n", rc));
1493 return VINF_EM_RAW_EMULATE_INSTR;
1494 }
1495
1496 Log(("IOM: rep ins%d port %#x count %d\n", cbTransfer * 8, uPort, cTransfers));
1497 if (cTransfers > 1)
1498 {
1499 /* If the device supports string transfers, ask it to do as
1500 * much as it wants. The rest is done with single-word transfers. */
1501 const RTGCUINTREG cTransfersOrg = cTransfers;
1502 rc = IOMIOPortReadString(pVM, uPort, &GCPtrDst, &cTransfers, cbTransfer);
1503 AssertRC(rc); Assert(cTransfers <= cTransfersOrg);
1504 pRegFrame->rdi += (cTransfersOrg - cTransfers) * cbTransfer;
1505 }
1506
1507#ifdef IN_RC
1508 MMGCRamRegisterTrapHandler(pVM);
1509#endif
1510
1511 while (cTransfers && rc == VINF_SUCCESS)
1512 {
1513 uint32_t u32Value;
1514 rc = IOMIOPortRead(pVM, uPort, &u32Value, cbTransfer);
1515 if (!IOM_SUCCESS(rc))
1516 break;
1517 int rc2 = iomRamWrite(pVM, GCPtrDst, &u32Value, cbTransfer);
1518 Assert(rc2 == VINF_SUCCESS); NOREF(rc2);
1519 GCPtrDst = (RTGCPTR)((RTGCUINTPTR)GCPtrDst + cbTransfer);
1520 pRegFrame->rdi += cbTransfer;
1521 cTransfers--;
1522 }
1523#ifdef IN_RC
1524 MMGCRamDeregisterTrapHandler(pVM);
1525#endif
1526
1527 /* Update ecx on exit. */
1528 if (uPrefix & PREFIX_REP)
1529 pRegFrame->ecx = cTransfers;
1530
1531 AssertMsg(rc == VINF_SUCCESS || rc == VINF_IOM_HC_IOPORT_READ || (rc >= VINF_EM_FIRST && rc <= VINF_EM_LAST) || RT_FAILURE(rc), ("%Rrc\n", rc));
1532 return rc;
1533}
1534
1535
1536/**
1537 * [REP*] INSB/INSW/INSD
1538 * ES:EDI,DX[,ECX]
1539 *
1540 * @returns Strict VBox status code. Informational status codes other than the one documented
1541 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
1542 * @retval VINF_SUCCESS Success.
1543 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
1544 * status code must be passed on to EM.
1545 * @retval VINF_IOM_HC_IOPORT_READ Defer the read to ring-3. (R0/GC only)
1546 * @retval VINF_EM_RAW_EMULATE_INSTR Defer the read to the REM.
1547 * @retval VINF_EM_RAW_GUEST_TRAP The exception was left pending. (TRPMRaiseXcptErr)
1548 * @retval VINF_TRPM_XCPT_DISPATCHED The exception was raised and dispatched for raw-mode execution. (TRPMRaiseXcptErr)
1549 * @retval VINF_EM_RESCHEDULE_REM The exception was dispatched and cannot be executed in raw-mode. (TRPMRaiseXcptErr)
1550 *
1551 * @param pVM The virtual machine.
1552 * @param pRegFrame Pointer to CPUMCTXCORE guest registers structure.
1553 * @param pCpu Disassembler CPU state.
1554 */
1555VMMDECL(int) IOMInterpretINS(PVM pVM, PCPUMCTXCORE pRegFrame, PDISCPUSTATE pCpu)
1556{
1557 /*
1558 * Get port number directly from the register (no need to bother the
1559 * disassembler). And get the I/O register size from the opcode / prefix.
1560 */
1561 RTIOPORT Port = pRegFrame->edx & 0xffff;
1562 unsigned cb = 0;
1563 if (pCpu->pCurInstr->opcode == OP_INSB)
1564 cb = 1;
1565 else
1566 cb = (pCpu->opmode == CPUMODE_16BIT) ? 2 : 4; /* dword in both 32 & 64 bits mode */
1567
1568 int rc = IOMInterpretCheckPortIOAccess(pVM, pRegFrame, Port, cb);
1569 if (RT_UNLIKELY(rc != VINF_SUCCESS))
1570 {
1571 AssertMsg(rc == VINF_EM_RAW_GUEST_TRAP || rc == VINF_TRPM_XCPT_DISPATCHED || rc == VINF_TRPM_XCPT_DISPATCHED || RT_FAILURE(rc), ("%Rrc\n", rc));
1572 return rc;
1573 }
1574
1575 return IOMInterpretINSEx(pVM, pRegFrame, Port, pCpu->prefix, cb);
1576}
1577
1578
1579/**
1580 * [REP*] OUTSB/OUTSW/OUTSD
1581 * DS:ESI,DX[,ECX]
1582 *
1583 * @remark Assumes caller checked the access privileges (IOMInterpretCheckPortIOAccess)
1584 *
1585 * @returns Strict VBox status code. Informational status codes other than the one documented
1586 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
1587 * @retval VINF_SUCCESS Success.
1588 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
1589 * status code must be passed on to EM.
1590 * @retval VINF_IOM_HC_IOPORT_WRITE Defer the write to ring-3. (R0/GC only)
1591 * @retval VINF_EM_RAW_GUEST_TRAP The exception was left pending. (TRPMRaiseXcptErr)
1592 * @retval VINF_TRPM_XCPT_DISPATCHED The exception was raised and dispatched for raw-mode execution. (TRPMRaiseXcptErr)
1593 * @retval VINF_EM_RESCHEDULE_REM The exception was dispatched and cannot be executed in raw-mode. (TRPMRaiseXcptErr)
1594 *
1595 * @param pVM The virtual machine.
1596 * @param pRegFrame Pointer to CPUMCTXCORE guest registers structure.
1597 * @param uPort IO Port
1598 * @param uPrefix IO instruction prefix
1599 * @param cbTransfer Size of transfer unit
1600 */
1601VMMDECL(int) IOMInterpretOUTSEx(PVM pVM, PCPUMCTXCORE pRegFrame, uint32_t uPort, uint32_t uPrefix, uint32_t cbTransfer)
1602{
1603#ifdef VBOX_WITH_STATISTICS
1604 STAM_COUNTER_INC(&pVM->iom.s.StatInstOuts);
1605#endif
1606
1607 /*
1608 * We do not support segment prefixes, REPNE or
1609 * decrementing source pointer.
1610 */
1611 if ( (uPrefix & (PREFIX_SEG | PREFIX_REPNE))
1612 || pRegFrame->eflags.Bits.u1DF)
1613 return VINF_EM_RAW_EMULATE_INSTR;
1614
1615 /*
1616 * Get bytes/words/dwords count to transfer.
1617 */
1618 RTGCUINTREG cTransfers = 1;
1619 if (uPrefix & PREFIX_REP)
1620 {
1621#ifndef IN_RC
1622 if ( CPUMIsGuestIn64BitCode(pVM, pRegFrame)
1623 && pRegFrame->rcx >= _4G)
1624 return VINF_EM_RAW_EMULATE_INSTR;
1625#endif
1626 cTransfers = pRegFrame->ecx;
1627 if (SELMGetCpuModeFromSelector(pVM, pRegFrame->eflags, pRegFrame->cs, &pRegFrame->csHid) == CPUMODE_16BIT)
1628 cTransfers &= 0xffff;
1629
1630 if (!cTransfers)
1631 return VINF_SUCCESS;
1632 }
1633
1634 /* Convert source address ds:esi. */
1635 RTGCPTR GCPtrSrc;
1636 int rc = SELMToFlatEx(pVM, DIS_SELREG_DS, pRegFrame, (RTGCPTR)pRegFrame->rsi,
1637 SELMTOFLAT_FLAGS_HYPER | SELMTOFLAT_FLAGS_NO_PL,
1638 &GCPtrSrc);
1639 if (RT_FAILURE(rc))
1640 {
1641 Log(("OUTS source address conversion failed -> fallback, rc=%Rrc\n", rc));
1642 return VINF_EM_RAW_EMULATE_INSTR;
1643 }
1644
1645 /* Access verification first; we currently can't recover properly from traps inside this instruction */
1646 uint32_t cpl = CPUMGetGuestCPL(pVM, pRegFrame);
1647 rc = PGMVerifyAccess(pVM, (RTGCUINTPTR)GCPtrSrc, cTransfers * cbTransfer,
1648 (cpl == 3) ? X86_PTE_US : 0);
1649 if (rc != VINF_SUCCESS)
1650 {
1651 Log(("OUTS will generate a trap -> fallback, rc=%Rrc\n", rc));
1652 return VINF_EM_RAW_EMULATE_INSTR;
1653 }
1654
1655 Log(("IOM: rep outs%d port %#x count %d\n", cbTransfer * 8, uPort, cTransfers));
1656 if (cTransfers > 1)
1657 {
1658 /*
1659 * If the device supports string transfers, ask it to do as
1660 * much as it wants. The rest is done with single-word transfers.
1661 */
1662 const RTGCUINTREG cTransfersOrg = cTransfers;
1663 rc = IOMIOPortWriteString(pVM, uPort, &GCPtrSrc, &cTransfers, cbTransfer);
1664 AssertRC(rc); Assert(cTransfers <= cTransfersOrg);
1665 pRegFrame->rsi += (cTransfersOrg - cTransfers) * cbTransfer;
1666 }
1667
1668#ifdef IN_RC
1669 MMGCRamRegisterTrapHandler(pVM);
1670#endif
1671
1672 while (cTransfers && rc == VINF_SUCCESS)
1673 {
1674 uint32_t u32Value;
1675 rc = iomRamRead(pVM, &u32Value, GCPtrSrc, cbTransfer);
1676 if (rc != VINF_SUCCESS)
1677 break;
1678 rc = IOMIOPortWrite(pVM, uPort, u32Value, cbTransfer);
1679 if (!IOM_SUCCESS(rc))
1680 break;
1681 GCPtrSrc = (RTGCPTR)((RTUINTPTR)GCPtrSrc + cbTransfer);
1682 pRegFrame->rsi += cbTransfer;
1683 cTransfers--;
1684 }
1685
1686#ifdef IN_RC
1687 MMGCRamDeregisterTrapHandler(pVM);
1688#endif
1689
1690 /* Update ecx on exit. */
1691 if (uPrefix & PREFIX_REP)
1692 pRegFrame->ecx = cTransfers;
1693
1694 AssertMsg(rc == VINF_SUCCESS || rc == VINF_IOM_HC_IOPORT_WRITE || (rc >= VINF_EM_FIRST && rc <= VINF_EM_LAST) || RT_FAILURE(rc), ("%Rrc\n", rc));
1695 return rc;
1696}
1697
1698
1699/**
1700 * [REP*] OUTSB/OUTSW/OUTSD
1701 * DS:ESI,DX[,ECX]
1702 *
1703 * @returns Strict VBox status code. Informational status codes other than the one documented
1704 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
1705 * @retval VINF_SUCCESS Success.
1706 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
1707 * status code must be passed on to EM.
1708 * @retval VINF_IOM_HC_IOPORT_WRITE Defer the write to ring-3. (R0/GC only)
1709 * @retval VINF_EM_RAW_EMULATE_INSTR Defer the write to the REM.
1710 * @retval VINF_EM_RAW_GUEST_TRAP The exception was left pending. (TRPMRaiseXcptErr)
1711 * @retval VINF_TRPM_XCPT_DISPATCHED The exception was raised and dispatched for raw-mode execution. (TRPMRaiseXcptErr)
1712 * @retval VINF_EM_RESCHEDULE_REM The exception was dispatched and cannot be executed in raw-mode. (TRPMRaiseXcptErr)
1713 *
1714 * @param pVM The virtual machine.
1715 * @param pRegFrame Pointer to CPUMCTXCORE guest registers structure.
1716 * @param pCpu Disassembler CPU state.
1717 */
1718VMMDECL(int) IOMInterpretOUTS(PVM pVM, PCPUMCTXCORE pRegFrame, PDISCPUSTATE pCpu)
1719{
1720 /*
1721 * Get port number from the first parameter.
1722 * And get the I/O register size from the opcode / prefix.
1723 */
1724 uint64_t Port = 0;
1725 unsigned cb = 0;
1726 bool fRc = iomGetRegImmData(pCpu, &pCpu->param1, pRegFrame, &Port, &cb);
1727 AssertMsg(fRc, ("Failed to get reg/imm port number!\n")); NOREF(fRc);
1728 if (pCpu->pCurInstr->opcode == OP_OUTSB)
1729 cb = 1;
1730 else
1731 cb = (pCpu->opmode == CPUMODE_16BIT) ? 2 : 4; /* dword in both 32 & 64 bits mode */
1732
1733 int rc = IOMInterpretCheckPortIOAccess(pVM, pRegFrame, Port, cb);
1734 if (RT_UNLIKELY(rc != VINF_SUCCESS))
1735 {
1736 AssertMsg(rc == VINF_EM_RAW_GUEST_TRAP || rc == VINF_TRPM_XCPT_DISPATCHED || rc == VINF_TRPM_XCPT_DISPATCHED || RT_FAILURE(rc), ("%Rrc\n", rc));
1737 return rc;
1738 }
1739
1740 return IOMInterpretOUTSEx(pVM, pRegFrame, Port, pCpu->prefix, cb);
1741}
1742
1743
1744#ifndef IN_RC
1745/**
1746 * Modify an existing MMIO region page; map to another guest physical region and change the access flags
1747 *
1748 * @returns VBox status code.
1749 *
1750 * @param pVM The virtual machine.
1751 * @param GCPhys Physical address that's part of the MMIO region to be changed.
1752 * @param GCPhysRemapped Remapped address.
1753 * @param fPageFlags Page flags to set (typically X86_PTE_RW).
1754 */
1755VMMDECL(int) IOMMMIOModifyPage(PVM pVM, RTGCPHYS GCPhys, RTGCPHYS GCPhysRemapped, uint64_t fPageFlags)
1756{
1757 Assert(fPageFlags == (X86_PTE_RW | X86_PTE_P));
1758
1759 Log(("IOMMMIOModifyPage %RGp -> %RGp flags=%RX64\n", GCPhys, GCPhysRemapped, fPageFlags));
1760
1761 /*
1762 * Lookup the current context range node and statistics.
1763 */
1764 PIOMMMIORANGE pRange = iomMMIOGetRange(&pVM->iom.s, GCPhys);
1765 AssertMsgReturn(pRange,
1766 ("Handlers and page tables are out of sync or something! GCPhys=%RGp\n", GCPhys),
1767 VERR_INTERNAL_ERROR);
1768
1769 GCPhys &= ~(RTGCPHYS)0xfff;
1770 GCPhysRemapped &= ~(RTGCPHYS)0xfff;
1771
1772 /* This currently only works in real mode, protected mode without paging or with nested paging. */
1773 if ( CPUMIsGuestInPagedProtectedMode(pVM)
1774 && !HWACCMIsNestedPagingActive(pVM))
1775 return VINF_SUCCESS; /* ignore */
1776
1777 int rc = PGMHandlerPhysicalPageAlias(pVM, pRange->GCPhys, GCPhys, GCPhysRemapped);
1778 AssertRCReturn(rc, rc);
1779
1780#ifdef VBOX_STRICT
1781 uint64_t fFlags;
1782 RTHCPHYS HCPhys;
1783 rc = PGMShwGetPage(pVM, (RTGCPTR)GCPhys, &fFlags, &HCPhys);
1784 Assert(rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
1785#endif
1786
1787 /* Mark it as writable and present so reads and writes no longer fault. */
1788 rc = PGMShwModifyPage(pVM, (RTGCPTR)GCPhys, 1, fPageFlags, ~fPageFlags);
1789 Assert(rc == VINF_SUCCESS || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
1790
1791 return VINF_SUCCESS;
1792}
1793
1794
1795/**
1796 * Reset a previously modified MMIO region; restore the access flags.
1797 *
1798 * @returns VBox status code.
1799 *
1800 * @param pVM The virtual machine.
1801 * @param GCPhys Physical address that's part of the MMIO region to be reset.
1802 */
1803VMMDECL(int) IOMMMIOResetRegion(PVM pVM, RTGCPHYS GCPhys)
1804{
1805 uint32_t cb;
1806
1807 Log(("IOMMMIOResetRegion %RGp\n", GCPhys));
1808
1809 /*
1810 * Lookup the current context range node and statistics.
1811 */
1812 PIOMMMIORANGE pRange = iomMMIOGetRange(&pVM->iom.s, GCPhys);
1813 AssertMsgReturn(pRange,
1814 ("Handlers and page tables are out of sync or something! GCPhys=%RGp\n", GCPhys),
1815 VERR_INTERNAL_ERROR);
1816
1817 /* This currently only works in real mode, protected mode without paging or with nested paging. */
1818 if ( CPUMIsGuestInPagedProtectedMode(pVM)
1819 && !HWACCMIsNestedPagingActive(pVM))
1820 return VINF_SUCCESS; /* ignore */
1821
1822
1823 cb = pRange->cb;
1824 GCPhys = pRange->GCPhys;
1825
1826 while (cb)
1827 {
1828 int rc = PGMHandlerPhysicalPageReset(pVM, pRange->GCPhys, GCPhys);
1829 AssertRC(rc);
1830
1831 /* Mark it as not present again to intercept all read and write access. */
1832 rc = PGMShwModifyPage(pVM, (RTGCPTR)GCPhys, 1, 0, ~(uint64_t)(X86_PTE_RW|X86_PTE_P));
1833 Assert(rc == VINF_SUCCESS || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
1834
1835#ifdef VBOX_STRICT
1836 uint64_t fFlags;
1837 RTHCPHYS HCPhys;
1838 rc = PGMShwGetPage(pVM, (RTGCPTR)GCPhys, &fFlags, &HCPhys);
1839 Assert(rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
1840#endif
1841 cb -= PAGE_SIZE;
1842 GCPhys += PAGE_SIZE;
1843 }
1844 return VINF_SUCCESS;
1845}
1846#endif /* !IN_RC */
1847
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