VirtualBox

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

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

Backed out previous changeset; no harm done there.

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