VirtualBox

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

Last change on this file since 23794 was 22493, checked in by vboxsync, 15 years ago

VMM,DevPCI,VBox/types.h: Added a VBOXSTRICTRC type for indicating strict VBox stuatus codes. Some expirmentation with making it a class in strict builds to get some help from the compiler with making sure the return code is treated correctly.

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