VirtualBox

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

Last change on this file since 8170 was 8155, checked in by vboxsync, 17 years ago

The Big Sun Rebranding Header Change

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