VirtualBox

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

Last change on this file since 2981 was 2981, checked in by vboxsync, 18 years ago

InnoTek -> innotek: all the headers and comments.

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