VirtualBox

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

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

#1865: Renamed PGMPhysReadGCPhys -> PGMPhysSimpleReadGCPhys, PGMPhysWriteGCPhys -> PGMPhysSimpleWriteGCPhys, PGMPhysReadGCPtrSafe -> PGMPhysReadGCPtr and PGMPhysWriteGCPtrSafe -> PGMPhysWriteGCPtr. This puts PGMPhysRead/Write and PGMPhysRead/WriteGCPtr in the same group.

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