VirtualBox

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

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

Biggest check-in ever. New source code headers for all (C) innotek files.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette