VirtualBox

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

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

Enable movs emulation for ring 0.

  • Property svn:keywords set to Id
File size: 46.0 KB
Line 
1/* $Id: IOMAllMMIO.cpp 2128 2007-04-17 12:42:00Z vboxsync $ */
2/** @file
3 * IOM - Input / Output Monitor - Guest Context.
4 */
5
6/*
7 * Copyright (C) 2006 InnoTek Systemberatung 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#ifndef IN_RING3
48
49/** @def IOMGC_MOVS_SUPPORT
50 * Define IOMGC_MOVS_SUPPORT for movsb/w/d support in GC and R0.
51 */
52#define IOMGC_MOVS_SUPPORT
53
54/*******************************************************************************
55* Internal Functions *
56*******************************************************************************/
57static bool iomGCGetRegImmData(PDISCPUSTATE pCpu, PCOP_PARAMETER pParam, PCPUMCTXCORE pRegFrame, uint32_t *pu32Data, unsigned *pcbSize);
58static bool iomGCSaveDataToReg(PDISCPUSTATE pCpu, PCOP_PARAMETER pParam, PCPUMCTXCORE pRegFrame, uint32_t u32Data);
59
60
61/*******************************************************************************
62* Global Variables *
63*******************************************************************************/
64/**
65 * Array for accessing 32-bit general registers in VMMREGFRAME structure
66 * by register's index from disasm.
67 */
68static unsigned g_aReg32Index[] =
69{
70 RT_OFFSETOF(CPUMCTXCORE, eax), /* USE_REG_EAX */
71 RT_OFFSETOF(CPUMCTXCORE, ecx), /* USE_REG_ECX */
72 RT_OFFSETOF(CPUMCTXCORE, edx), /* USE_REG_EDX */
73 RT_OFFSETOF(CPUMCTXCORE, ebx), /* USE_REG_EBX */
74 RT_OFFSETOF(CPUMCTXCORE, esp), /* USE_REG_ESP */
75 RT_OFFSETOF(CPUMCTXCORE, ebp), /* USE_REG_EBP */
76 RT_OFFSETOF(CPUMCTXCORE, esi), /* USE_REG_ESI */
77 RT_OFFSETOF(CPUMCTXCORE, edi) /* USE_REG_EDI */
78};
79
80/**
81 * Macro for accessing 32-bit general purpose registers in CPUMCTXCORE structure.
82 */
83#define ACCESS_REG32(p, idx) (*((uint32_t *)((char *)(p) + g_aReg32Index[idx])))
84
85/**
86 * Array for accessing 16-bit general registers in CPUMCTXCORE structure
87 * by register's index from disasm.
88 */
89static unsigned g_aReg16Index[] =
90{
91 RT_OFFSETOF(CPUMCTXCORE, eax), /* USE_REG_AX */
92 RT_OFFSETOF(CPUMCTXCORE, ecx), /* USE_REG_CX */
93 RT_OFFSETOF(CPUMCTXCORE, edx), /* USE_REG_DX */
94 RT_OFFSETOF(CPUMCTXCORE, ebx), /* USE_REG_BX */
95 RT_OFFSETOF(CPUMCTXCORE, esp), /* USE_REG_SP */
96 RT_OFFSETOF(CPUMCTXCORE, ebp), /* USE_REG_BP */
97 RT_OFFSETOF(CPUMCTXCORE, esi), /* USE_REG_SI */
98 RT_OFFSETOF(CPUMCTXCORE, edi) /* USE_REG_DI */
99};
100
101/**
102 * Macro for accessing 16-bit general purpose registers in CPUMCTXCORE structure.
103 */
104#define ACCESS_REG16(p, idx) (*((uint16_t *)((char *)(p) + g_aReg16Index[idx])))
105
106/**
107 * Array for accessing 8-bit general registers in CPUMCTXCORE structure
108 * by register's index from disasm.
109 */
110static unsigned g_aReg8Index[] =
111{
112 RT_OFFSETOF(CPUMCTXCORE, eax), /* USE_REG_AL */
113 RT_OFFSETOF(CPUMCTXCORE, ecx), /* USE_REG_CL */
114 RT_OFFSETOF(CPUMCTXCORE, edx), /* USE_REG_DL */
115 RT_OFFSETOF(CPUMCTXCORE, ebx), /* USE_REG_BL */
116 RT_OFFSETOF(CPUMCTXCORE, eax) + 1, /* USE_REG_AH */
117 RT_OFFSETOF(CPUMCTXCORE, ecx) + 1, /* USE_REG_CH */
118 RT_OFFSETOF(CPUMCTXCORE, edx) + 1, /* USE_REG_DH */
119 RT_OFFSETOF(CPUMCTXCORE, ebx) + 1 /* USE_REG_BH */
120};
121
122/**
123 * Macro for accessing 8-bit general purpose registers in CPUMCTXCORE structure.
124 */
125#define ACCESS_REG8(p, idx) (*((uint8_t *)((char *)(p) + g_aReg8Index[idx])))
126
127/**
128 * Array for accessing segment registers in CPUMCTXCORE structure
129 * by register's index from disasm.
130 */
131static unsigned g_aRegSegIndex[] =
132{
133 RT_OFFSETOF(CPUMCTXCORE, es), /* USE_REG_ES */
134 RT_OFFSETOF(CPUMCTXCORE, cs), /* USE_REG_CS */
135 RT_OFFSETOF(CPUMCTXCORE, ss), /* USE_REG_SS */
136 RT_OFFSETOF(CPUMCTXCORE, ds), /* USE_REG_DS */
137 RT_OFFSETOF(CPUMCTXCORE, fs), /* USE_REG_FS */
138 RT_OFFSETOF(CPUMCTXCORE, gs) /* USE_REG_GS */
139};
140
141/**
142 * Macro for accessing segment registers in CPUMCTXCORE structure.
143 */
144#define ACCESS_REGSEG(p, idx) (*((uint16_t *)((char *)(p) + g_aRegSegIndex[idx])))
145
146/**
147 * Array for fast recode of the operand size (1/2/4/8 bytes) to bit shift value.
148 */
149static const unsigned g_aSize2Shift[] =
150{
151 ~0, /* 0 - invalid */
152 0, /* *1 == 2^0 */
153 1, /* *2 == 2^1 */
154 ~0, /* 3 - invalid */
155 2, /* *4 == 2^2 */
156 ~0, /* 5 - invalid */
157 ~0, /* 6 - invalid */
158 ~0, /* 7 - invalid */
159 3 /* *8 == 2^3 */
160};
161
162/**
163 * Macro for fast recode of the operand size (1/2/4/8 bytes) to bit shift value.
164 */
165#define SIZE2SHIFT(cb) (g_aSize2Shift[cb])
166
167
168/**
169 * Wrapper which does the write and updates range statistics when such are enabled.
170 * @warning VBOX_SUCCESS(rc=VINF_IOM_HC_MMIO_WRITE) is TRUE!
171 */
172inline int iomGCMMIODoWrite(PVM pVM, CTXALLSUFF(PIOMMMIORANGE) pRange, RTGCPHYS GCPhysFault, const void *pvData, unsigned cbSize)
173{
174#ifdef VBOX_WITH_STATISTICS
175 if (pRange->cbSize <= PAGE_SIZE)
176 {
177 PIOMMMIOSTATS pStats = iomMMIOGetStats(&pVM->iom.s, GCPhysFault);
178 if (!pStats)
179 return VINF_IOM_HC_MMIO_WRITE;
180
181 int rc = pRange->pfnWriteCallback(pRange->pDevIns, pRange->pvUser, GCPhysFault, (void *)pvData, cbSize); /* @todo fix const!! */
182 if (rc != VINF_IOM_HC_MMIO_WRITE)
183 STAM_COUNTER_INC(&pStats->WriteGC);
184 return rc;
185 }
186#endif
187 return pRange->pfnWriteCallback(pRange->pDevIns, pRange->pvUser, GCPhysFault, (void *)pvData, cbSize);
188}
189
190/**
191 * Wrapper which does the read and updates range statistics when such are enabled.
192 */
193inline int iomGCMMIODoRead(PVM pVM, CTXALLSUFF(PIOMMMIORANGE) pRange, RTGCPHYS GCPhysFault, void *pvData, unsigned cbSize)
194{
195#ifdef VBOX_WITH_STATISTICS
196 if (pRange->cbSize <= PAGE_SIZE)
197 {
198 PIOMMMIOSTATS pStats = iomMMIOGetStats(&pVM->iom.s, GCPhysFault);
199 if (!pStats)
200 return VINF_IOM_HC_MMIO_READ;
201
202 int rc = pRange->pfnReadCallback(pRange->pDevIns, pRange->pvUser, GCPhysFault, pvData, cbSize);
203 if (rc != VINF_IOM_HC_MMIO_READ)
204 STAM_COUNTER_INC(&pStats->ReadGC);
205 return rc;
206 }
207#endif
208 return pRange->pfnReadCallback(pRange->pDevIns, pRange->pvUser, GCPhysFault, pvData, cbSize);
209}
210
211
212/**
213 * Returns the contents of register or immediate data of instruction's parameter.
214 *
215 * @returns true on success.
216 *
217 * @param pCpu Pointer to current disassembler context.
218 * @param pParam Pointer to parameter of instruction to proccess.
219 * @param pRegFrame Pointer to CPUMCTXCORE guest structure.
220 * @param pu32Data Where to store retrieved data.
221 * @param pcbSize Where to store the size of data (1, 2, 4).
222 */
223static bool iomGCGetRegImmData(PDISCPUSTATE pCpu, PCOP_PARAMETER pParam, PCPUMCTXCORE pRegFrame, uint32_t *pu32Data, unsigned *pcbSize)
224{
225 if (pParam->flags & (USE_BASE | USE_INDEX | USE_SCALE | USE_DISPLACEMENT8 | USE_DISPLACEMENT16 | USE_DISPLACEMENT32))
226 {
227 *pcbSize = 0;
228 *pu32Data = 0;
229 return false;
230 }
231
232 if (pParam->flags & USE_REG_GEN32)
233 {
234 *pcbSize = 4;
235 *pu32Data = ACCESS_REG32(pRegFrame, pParam->base.reg_gen32);
236 return true;
237 }
238
239 if (pParam->flags & USE_REG_GEN16)
240 {
241 *pcbSize = 2;
242 *pu32Data = ACCESS_REG16(pRegFrame, pParam->base.reg_gen16);
243 return true;
244 }
245
246 if (pParam->flags & USE_REG_GEN8)
247 {
248 *pcbSize = 1;
249 *pu32Data = ACCESS_REG8(pRegFrame, pParam->base.reg_gen8);
250 return true;
251 }
252
253 if (pParam->flags & (USE_IMMEDIATE32|USE_IMMEDIATE32_SX8))
254 {
255 *pcbSize = 4;
256 *pu32Data = (uint32_t)pParam->parval;
257 return true;
258 }
259
260 if (pParam->flags & (USE_IMMEDIATE16|USE_IMMEDIATE16_SX8))
261 {
262 *pcbSize = 2;
263 *pu32Data = (uint16_t)pParam->parval;
264 return true;
265 }
266
267 if (pParam->flags & USE_IMMEDIATE8)
268 {
269 *pcbSize = 1;
270 *pu32Data = (uint8_t)pParam->parval;
271 return true;
272 }
273
274 if (pParam->flags & USE_REG_SEG)
275 {
276 *pcbSize = 2;
277 *pu32Data = ACCESS_REGSEG(pRegFrame, pParam->base.reg_seg);
278 return true;
279 } /* Else - error. */
280
281 *pcbSize = 0;
282 *pu32Data = 0;
283 return false;
284}
285
286
287/**
288 * Saves data to 8/16/32 general purpose or segment register defined by
289 * instruction's parameter.
290 *
291 * @returns true on success.
292 * @param pCpu Pointer to current disassembler context.
293 * @param pParam Pointer to parameter of instruction to proccess.
294 * @param pRegFrame Pointer to CPUMCTXCORE guest structure.
295 * @param u32Data 8/16/32 bit data to store.
296 */
297static bool iomGCSaveDataToReg(PDISCPUSTATE pCpu, PCOP_PARAMETER pParam, PCPUMCTXCORE pRegFrame, unsigned u32Data)
298{
299 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))
300 {
301 return false;
302 }
303
304 if (pParam->flags & USE_REG_GEN32)
305 {
306 ACCESS_REG32(pRegFrame, pParam->base.reg_gen32) = u32Data;
307 return true;
308 }
309
310 if (pParam->flags & USE_REG_GEN16)
311 {
312 ACCESS_REG16(pRegFrame, pParam->base.reg_gen16) = (uint16_t)u32Data;
313 return true;
314 }
315
316 if (pParam->flags & USE_REG_GEN8)
317 {
318 ACCESS_REG8(pRegFrame, pParam->base.reg_gen8) = (uint8_t)u32Data;
319 return true;
320 }
321
322 if (pParam->flags & USE_REG_SEG)
323 {
324 ACCESS_REGSEG(pRegFrame, pParam->base.reg_seg) = (uint16_t)u32Data;
325 return true;
326 }
327
328 /* Else - error. */
329 return false;
330}
331
332
333/*
334 * Internal - statistics only.
335 */
336inline void iomGCMMIOStatLength(PVM pVM, unsigned cb)
337{
338#ifdef VBOX_WITH_STATISTICS
339 switch (cb)
340 {
341 case 1:
342 STAM_COUNTER_INC(&pVM->iom.s.StatGCMMIO1Byte);
343 break;
344 case 2:
345 STAM_COUNTER_INC(&pVM->iom.s.StatGCMMIO2Bytes);
346 break;
347 case 4:
348 STAM_COUNTER_INC(&pVM->iom.s.StatGCMMIO4Bytes);
349 break;
350 default:
351 /* No way. */
352 AssertMsgFailed(("Invalid data length %d\n", cb));
353 break;
354 }
355#else
356 NOREF(pVM); NOREF(cb);
357#endif
358}
359
360
361/**
362 * MOV reg, mem (read)
363 * MOVZX reg, mem (read)
364 * MOVSX reg, mem (read)
365 *
366 * @returns VBox status code.
367 *
368 * @param pVM The virtual machine (GC pointer ofcourse).
369 * @param pRegFrame Pointer to CPUMCTXCORE guest registers structure.
370 * @param pCpu Disassembler CPU state.
371 * @param pRange Pointer MMIO range.
372 * @param GCPhysFault The GC physical address corresponding to pvFault.
373 */
374static int iomGCInterpretMOVxXRead(PVM pVM, PCPUMCTXCORE pRegFrame, PDISCPUSTATE pCpu, CTXALLSUFF(PIOMMMIORANGE) pRange, RTGCPHYS GCPhysFault)
375{
376 /*
377 * If no read handler then go to ring-3 and handle it there.
378 */
379 if (!pRange->pfnReadCallback)
380 return VINF_IOM_HC_MMIO_READ;
381
382 /*
383 * Get the data size from parameter 2,
384 * and call the handler function to get the data.
385 */
386 unsigned cbSize = DISGetParamSize(pCpu, &pCpu->param2);
387 AssertMsg(cbSize > 0 && cbSize <= sizeof(uint32_t), ("cbSize=%d\n", cbSize));
388
389 uint32_t u32Data = 0;
390 int rc = iomGCMMIODoRead(pVM, pRange, GCPhysFault, &u32Data, cbSize);
391 if (rc == VINF_SUCCESS)
392 {
393 /*
394 * Do sign extension for MOVSX.
395 */
396 /** @todo checkup MOVSX implementation! */
397 if (pCpu->pCurInstr->opcode == OP_MOVSX)
398 {
399 if (cbSize == 1)
400 {
401 /* DWORD <- BYTE */
402 int32_t iData = (int8_t)u32Data;
403 u32Data = (uint32_t)iData;
404 }
405 else
406 {
407 /* DWORD <- WORD */
408 int32_t iData = (int16_t)u32Data;
409 u32Data = (uint32_t)iData;
410 }
411 }
412
413 /*
414 * Store the result to register (parameter 1).
415 */
416 bool fRc = iomGCSaveDataToReg(pCpu, &pCpu->param1, pRegFrame, u32Data);
417 AssertMsg(fRc, ("Failed to store register value!\n")); NOREF(fRc);
418 }
419
420 if (rc == VINF_SUCCESS)
421 iomGCMMIOStatLength(pVM, cbSize);
422 return rc;
423}
424
425
426/**
427 * MOV mem, reg|imm (write)
428 *
429 * @returns VBox status code.
430 *
431 * @param pVM The virtual machine (GC pointer ofcourse).
432 * @param pRegFrame Pointer to CPUMCTXCORE guest registers structure.
433 * @param pCpu Disassembler CPU state.
434 * @param pRange Pointer MMIO range.
435 * @param GCPhysFault The GC physical address corresponding to pvFault.
436 */
437static int iomGCInterpretMOVxXWrite(PVM pVM, PCPUMCTXCORE pRegFrame, PDISCPUSTATE pCpu, CTXALLSUFF(PIOMMMIORANGE) pRange, RTGCPHYS GCPhysFault)
438{
439 /*
440 * If no write handler then go to ring-3 and handle it there.
441 */
442 if (!pRange->pfnWriteCallback)
443 return VINF_IOM_HC_MMIO_WRITE;
444
445 /*
446 * Get data to write from second parameter,
447 * and call the callback to write it.
448 */
449 unsigned cbSize = 0;
450 uint32_t u32Data = 0;
451 bool fRc = iomGCGetRegImmData(pCpu, &pCpu->param2, pRegFrame, &u32Data, &cbSize);
452 AssertMsg(fRc, ("Failed to get reg/imm port number!\n")); NOREF(fRc);
453
454 int rc = iomGCMMIODoWrite(pVM, pRange, GCPhysFault, &u32Data, cbSize);
455 if (rc == VINF_SUCCESS)
456 iomGCMMIOStatLength(pVM, cbSize);
457 return rc;
458}
459
460
461/** @todo All the string MMIO stuff can do terrible things since physical contiguous mappings are
462 * assumed all over the place! This must be addressed in a general way, like for example let EM do
463 * all the interpretation and checking of selectors and addresses.
464 *
465 * -> I don't see the problem here. MMIO ranges are by definition linear ranges. The virtual source or destination is read/written properly.
466 */
467
468
469#ifdef IOMGC_MOVS_SUPPORT
470
471inline int iomRamRead(PVM pVM, void *pDest, RTGCPTR GCSrc, uint32_t cb)
472{
473#ifdef IN_GC
474 return MMGCRamReadNoTrapHandler(pDest, GCSrc, cb);
475#else
476 int rc;
477 RTGCPHYS GCPhys;
478 RTGCUINTPTR offset;
479
480 offset = GCSrc & PAGE_OFFSET_MASK;
481
482 /** @todo optimize the loop; no need to convert the address all the time */
483 rc = PGMPhysGCPtr2GCPhys(pVM, GCSrc, &GCPhys);
484 AssertRCReturn(rc, rc);
485 PGMPhysRead(pVM, GCPhys + offset, pDest, cb);
486 return VINF_SUCCESS;
487#endif
488}
489
490inline int iomRamWrite(PVM pVM, RTGCPTR GCDest, void *pSrc, uint32_t cb)
491{
492#ifdef IN_GC
493 return MMGCRamWriteNoTrapHandler(GCDest, pSrc, cb);
494#else
495 int rc;
496 RTGCPHYS GCPhys;
497 RTGCUINTPTR offset;
498
499 /** @todo optimize the loop; no need to convert the address all the time */
500 offset = GCDest & PAGE_OFFSET_MASK;
501 rc = PGMPhysGCPtr2GCPhys(pVM, GCDest, &GCPhys);
502 AssertRCReturn(rc, rc);
503 PGMPhysWrite(pVM, GCPhys + offset, pSrc, cb);
504 return VINF_SUCCESS;
505#endif
506}
507
508/**
509 * [REP] MOVSB
510 * [REP] MOVSW
511 * [REP] MOVSD
512 *
513 * Restricted implementation.
514 *
515 *
516 * @returns VBox status code.
517 *
518 * @param pVM The virtual machine (GC pointer ofcourse).
519 * @param uErrorCode CPU Error code.
520 * @param pRegFrame Trap register frame.
521 * @param GCPhysFault The GC physical address corresponding to pvFault.
522 * @param pCpu Disassembler CPU state.
523 * @param pRange Pointer MMIO range.
524 */
525static int iomGCInterpretMOVS(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPHYS GCPhysFault, PDISCPUSTATE pCpu, CTXALLSUFF(PIOMMMIORANGE) pRange)
526{
527 STAM_PROFILE_START(&pVM->iom.s.StatGCInstMovs, a);
528
529 /*
530 * We do not support segment prefixes or REPNE.
531 */
532 if (pCpu->prefix & (PREFIX_SEG | PREFIX_REPNE))
533 return VINF_IOM_HC_MMIO_READ_WRITE;
534
535
536 /*
537 * Get bytes/words/dwords count to copy.
538 */
539 uint32_t cTransfers = 1;
540 if (pCpu->prefix & PREFIX_REP)
541 {
542 cTransfers = pRegFrame->ecx;
543 if (!cTransfers)
544 return VINF_SUCCESS;
545 }
546
547 /* Get the current privilege level. */
548 uint32_t cpl = CPUMGetGuestCPL(pVM, pRegFrame);
549
550 /*
551 * Get data size.
552 */
553 unsigned cbSize = DISGetParamSize(pCpu, &pCpu->param1);
554 Assert(cbSize);
555 int offIncrement = pRegFrame->eflags.Bits.u1DF ? -(signed)cbSize : (signed)cbSize;
556
557#ifdef VBOX_WITH_STATISTICS
558 if (pVM->iom.s.cMovsMaxBytes < (cTransfers << SIZE2SHIFT(cbSize)))
559 pVM->iom.s.cMovsMaxBytes = cTransfers << SIZE2SHIFT(cbSize);
560#endif
561
562 RTGCPHYS Phys = GCPhysFault;
563 int rc;
564 if (uErrorCode & X86_TRAP_PF_RW)
565 {
566 /*
567 * Write operation: [Mem] -> [MMIO]
568 * ds:esi (Virt Src) -> es:edi (Phys Dst)
569 */
570 STAM_PROFILE_START(&pVM->iom.s.StatGCInstMovsToMMIO, a2);
571
572 /* Check callback. */
573 if (!pRange->pfnWriteCallback)
574 {
575 STAM_PROFILE_STOP(&pVM->iom.s.StatGCInstMovsToMMIO, a2);
576 return VINF_IOM_HC_MMIO_WRITE;
577 }
578
579 /* Convert source address ds:esi. */
580 RTGCUINTPTR pu8Virt;
581 rc = SELMToFlatEx(pVM, pRegFrame->eflags, pRegFrame->ds, (RTGCPTR)pRegFrame->esi, &pRegFrame->dsHid,
582 SELMTOFLAT_FLAGS_HYPER | SELMTOFLAT_FLAGS_NO_PL,
583 (PRTGCPTR)&pu8Virt, NULL);
584 if (VBOX_SUCCESS(rc))
585 {
586
587 /* Access verification first; we currently can't recover properly from traps inside this instruction */
588 rc = PGMVerifyAccess(pVM, pu8Virt, cTransfers * cbSize, (cpl == 3) ? X86_PTE_US : 0);
589 if (rc != VINF_SUCCESS)
590 {
591 Log(("MOVS will generate a trap -> recompiler, rc=%d\n", rc));
592 STAM_PROFILE_STOP(&pVM->iom.s.StatGCInstMovsToMMIO, a2);
593 return VINF_EM_RAW_EMULATE_INSTR;
594 }
595
596#ifdef IN_GC
597 MMGCRamRegisterTrapHandler(pVM);
598#endif
599
600 /* copy loop. */
601 while (cTransfers)
602 {
603 uint32_t u32Data = 0;
604 rc = iomRamRead(pVM, &u32Data, (RTGCPTR)pu8Virt, cbSize);
605 if (rc != VINF_SUCCESS)
606 break;
607 rc = iomGCMMIODoWrite(pVM, pRange, Phys, &u32Data, cbSize);
608 if (rc != VINF_SUCCESS)
609 break;
610
611 pu8Virt += offIncrement;
612 Phys += offIncrement;
613 pRegFrame->esi += offIncrement;
614 pRegFrame->edi += offIncrement;
615 cTransfers--;
616 }
617#ifdef IN_GC
618 MMGCRamDeregisterTrapHandler(pVM);
619#endif
620 /* Update ecx. */
621 if (pCpu->prefix & PREFIX_REP)
622 pRegFrame->ecx = cTransfers;
623 STAM_PROFILE_STOP(&pVM->iom.s.StatGCInstMovsToMMIO, a2);
624 }
625 else
626 rc = VINF_IOM_HC_MMIO_READ_WRITE;
627 }
628 else
629 {
630 /*
631 * Read operation: [MMIO] -> [mem] or [MMIO] -> [MMIO]
632 * ds:[eSI] (Phys Src) -> es:[eDI] (Virt Dst)
633 */
634 /* Check callback. */
635 if (!pRange->pfnReadCallback)
636 return VINF_IOM_HC_MMIO_READ;
637
638 /* Convert destination address. */
639 RTGCUINTPTR pu8Virt;
640 rc = SELMToFlatEx(pVM, pRegFrame->eflags, pRegFrame->es, (RTGCPTR)pRegFrame->edi, &pRegFrame->esHid,
641 SELMTOFLAT_FLAGS_HYPER | SELMTOFLAT_FLAGS_NO_PL,
642 (RTGCPTR *)&pu8Virt, NULL);
643 if (VBOX_FAILURE(rc))
644 return VINF_EM_RAW_GUEST_TRAP;
645
646 /* Check if destination address is MMIO. */
647 RTGCPHYS PhysDst;
648 rc = PGMGstGetPage(pVM, (RTGCPTR)pu8Virt, NULL, &PhysDst);
649 if ( VBOX_SUCCESS(rc)
650 && iomMMIOGetRangeHC(&pVM->iom.s, PhysDst))
651 {
652 /*
653 * Extra: [MMIO] -> [MMIO]
654 */
655 STAM_PROFILE_START(&pVM->iom.s.StatGCInstMovsMMIO, d);
656 STAM_PROFILE_START(&pVM->iom.s.StatGCInstMovsFromMMIO, c);
657
658 PhysDst |= (RTGCUINTPTR)pu8Virt & PAGE_OFFSET_MASK;
659 CTXALLSUFF(PIOMMMIORANGE) pMMIODst = iomMMIOGetRange(&pVM->iom.s, PhysDst);
660 if ( !pMMIODst
661 || !pMMIODst->pfnWriteCallback)
662 {
663 STAM_PROFILE_STOP(&pVM->iom.s.StatGCInstMovsMMIO, d);
664 STAM_PROFILE_STOP(&pVM->iom.s.StatGCInstMovsFromMMIO, c);
665 return VINF_IOM_HC_MMIO_READ_WRITE;
666 }
667
668 /* copy loop. */
669 while (cTransfers)
670 {
671 uint32_t u32Data;
672 rc = iomGCMMIODoRead(pVM, pRange, Phys, &u32Data, cbSize);
673 if (rc != VINF_SUCCESS)
674 break;
675 rc = iomGCMMIODoWrite(pVM, pMMIODst, PhysDst, &u32Data, cbSize);
676 if (rc != VINF_SUCCESS)
677 break;
678
679 Phys += offIncrement;
680 PhysDst += offIncrement;
681 pRegFrame->esi += offIncrement;
682 pRegFrame->edi += offIncrement;
683 cTransfers--;
684 }
685 STAM_PROFILE_STOP(&pVM->iom.s.StatGCInstMovsMMIO, d);
686 STAM_PROFILE_STOP(&pVM->iom.s.StatGCInstMovsFromMMIO, c);
687 }
688 else
689 {
690 /*
691 * Normal: [MMIO] -> [Mem]
692 */
693 STAM_PROFILE_START(&pVM->iom.s.StatGCInstMovsFromMMIO, c);
694
695 /* Access verification first; we currently can't recover properly from traps inside this instruction */
696 rc = PGMVerifyAccess(pVM, pu8Virt, cTransfers * cbSize, X86_PTE_RW | ((cpl == 3) ? X86_PTE_US : 0));
697 if (rc != VINF_SUCCESS)
698 {
699 Log(("MOVS will generate a trap -> recompiler, rc=%d\n", rc));
700 STAM_PROFILE_STOP(&pVM->iom.s.StatGCInstMovsFromMMIO, c);
701 return VINF_EM_RAW_EMULATE_INSTR;
702 }
703
704 /* copy loop. */
705#ifdef IN_GC
706 MMGCRamRegisterTrapHandler(pVM);
707#endif
708 while (cTransfers)
709 {
710 uint32_t u32Data;
711 rc = iomGCMMIODoRead(pVM, pRange, Phys, &u32Data, cbSize);
712 if (rc != VINF_SUCCESS)
713 break;
714 rc = iomRamWrite(pVM, (RTGCPTR)pu8Virt, &u32Data, cbSize);
715 if (rc != VINF_SUCCESS)
716 {
717 Log(("MMGCRamWriteNoTrapHandler %08X size=%d failed with %d\n", pu8Virt, cbSize, rc));
718 break;
719 }
720
721 pu8Virt += offIncrement;
722 Phys += offIncrement;
723 pRegFrame->esi += offIncrement;
724 pRegFrame->edi += offIncrement;
725 cTransfers--;
726 }
727#ifdef IN_GC
728 MMGCRamDeregisterTrapHandler(pVM);
729#endif
730 STAM_PROFILE_STOP(&pVM->iom.s.StatGCInstMovsFromMMIO, c);
731 }
732
733 /* Update ecx on exit. */
734 if (pCpu->prefix & PREFIX_REP)
735 pRegFrame->ecx = cTransfers;
736 }
737
738 /* work statistics. */
739 if (rc == VINF_SUCCESS)
740 {
741 STAM_PROFILE_STOP(&pVM->iom.s.StatGCInstMovs, a);
742 iomGCMMIOStatLength(pVM, cbSize);
743 }
744 return rc;
745}
746#endif /* IOMGC_MOVS_SUPPORT */
747
748
749
750/**
751 * [REP] STOSB
752 * [REP] STOSW
753 * [REP] STOSD
754 *
755 * Restricted implementation.
756 *
757 *
758 * @returns VBox status code.
759 *
760 * @param pVM The virtual machine (GC pointer ofcourse).
761 * @param pRegFrame Trap register frame.
762 * @param GCPhysFault The GC physical address corresponding to pvFault.
763 * @param pCpu Disassembler CPU state.
764 * @param pRange Pointer MMIO range.
765 */
766static int iomGCInterpretSTOS(PVM pVM, PCPUMCTXCORE pRegFrame, RTGCPHYS GCPhysFault, PDISCPUSTATE pCpu, CTXALLSUFF(PIOMMMIORANGE) pRange)
767{
768 STAM_PROFILE_START(&pVM->iom.s.StatGCInstStos, a);
769
770 /*
771 * We do not support segment prefixes or REPNE..
772 */
773 if (pCpu->prefix & (PREFIX_SEG | PREFIX_REPNE))
774 return VINF_IOM_HC_MMIO_READ_WRITE;
775
776 /*
777 * Get bytes/words/dwords count to copy.
778 */
779 uint32_t cTransfers = 1;
780 if (pCpu->prefix & PREFIX_REP)
781 {
782 cTransfers = pRegFrame->ecx;
783 if (!cTransfers)
784 return VINF_SUCCESS;
785 }
786
787 /*
788 * Get data size.
789 */
790 unsigned cbSize = DISGetParamSize(pCpu, &pCpu->param1);
791 Assert(cbSize);
792 int offIncrement = pRegFrame->eflags.Bits.u1DF ? -(signed)cbSize : (signed)cbSize;
793
794#ifdef VBOX_WITH_STATISTICS
795 if (pVM->iom.s.cStosMaxBytes < (cTransfers << SIZE2SHIFT(cbSize)))
796 pVM->iom.s.cStosMaxBytes = cTransfers << SIZE2SHIFT(cbSize);
797#endif
798
799
800 RTGCPHYS Phys = GCPhysFault;
801 uint32_t u32Data = pRegFrame->eax;
802 int rc;
803 if (pRange->pfnFillCallback)
804 {
805 /*
806 * Use the fill callback.
807 */
808 /** @todo pfnFillCallback must return number of bytes successfully written!!! */
809 if (offIncrement > 0)
810 {
811 /* addr++ variant. */
812 rc = pRange->pfnFillCallback(pRange->pDevIns, pRange->pvUser, Phys, u32Data, cbSize, cTransfers);
813 if (rc == VINF_SUCCESS)
814 {
815 /* Update registers. */
816 pRegFrame->edi += cTransfers << SIZE2SHIFT(cbSize);
817 if (pCpu->prefix & PREFIX_REP)
818 pRegFrame->ecx = 0;
819 }
820 }
821 else
822 {
823 /* addr-- variant. */
824 rc = pRange->pfnFillCallback(pRange->pDevIns, pRange->pvUser, (Phys - (cTransfers - 1)) << SIZE2SHIFT(cbSize), u32Data, cbSize, cTransfers);
825 if (rc == VINF_SUCCESS)
826 {
827 /* Update registers. */
828 pRegFrame->edi -= cTransfers << SIZE2SHIFT(cbSize);
829 if (pCpu->prefix & PREFIX_REP)
830 pRegFrame->ecx = 0;
831 }
832 }
833 }
834 else
835 {
836 /*
837 * Use the write callback.
838 */
839 /* Check write callback. */
840 if (!pRange->pfnWriteCallback)
841 return VINF_IOM_HC_MMIO_WRITE;
842
843 /* fill loop. */
844 do
845 {
846 rc = iomGCMMIODoWrite(pVM, pRange, Phys, &u32Data, cbSize);
847 if (rc != VINF_SUCCESS)
848 break;
849
850 Phys += offIncrement;
851 pRegFrame->edi += offIncrement;
852 cTransfers--;
853 } while (cTransfers);
854
855 /* Update ecx on exit. */
856 if (pCpu->prefix & PREFIX_REP)
857 pRegFrame->ecx = cTransfers;
858 }
859
860 /*
861 * Work statistics and return.
862 */
863 if (rc == VINF_SUCCESS)
864 {
865 STAM_PROFILE_STOP(&pVM->iom.s.StatGCInstStos, a);
866 iomGCMMIOStatLength(pVM, cbSize);
867 }
868 return rc;
869}
870
871
872/**
873 * [REP] LODSB
874 * [REP] LODSW
875 * [REP] LODSD
876 *
877 * Restricted implementation.
878 *
879 *
880 * @returns VBox status code.
881 *
882 * @param pVM The virtual machine (GC pointer ofcourse).
883 * @param pRegFrame Trap register frame.
884 * @param GCPhysFault The GC physical address corresponding to pvFault.
885 * @param pCpu Disassembler CPU state.
886 * @param pRange Pointer MMIO range.
887 */
888static int iomGCInterpretLODS(PVM pVM, PCPUMCTXCORE pRegFrame, RTGCPHYS GCPhysFault, PDISCPUSTATE pCpu, CTXALLSUFF(PIOMMMIORANGE) pRange)
889{
890 STAM_PROFILE_START(&pVM->iom.s.StatGCInstLods, a1);
891
892 /*
893 * We do not support segment prefixes or REP*.
894 */
895 if (pCpu->prefix & (PREFIX_SEG | PREFIX_REP | PREFIX_REPNE))
896 return VINF_IOM_HC_MMIO_READ_WRITE;
897
898 /* Check that we can handle it. */
899 if (!pRange->pfnReadCallback)
900 return VINF_IOM_HC_MMIO_READ;
901
902 /*
903 * Get data size.
904 */
905 unsigned cbSize = DISGetParamSize(pCpu, &pCpu->param2);
906 Assert(cbSize);
907 int offIncrement = pRegFrame->eflags.Bits.u1DF ? -(signed)cbSize : (signed)cbSize;
908
909 /*
910 * Perform read.
911 */
912 int rc = iomGCMMIODoRead(pVM, pRange, GCPhysFault, &pRegFrame->eax, cbSize);
913 if (rc == VINF_SUCCESS)
914 pRegFrame->esi += offIncrement;
915
916 /*
917 * Work statistics and return.
918 */
919 if (rc == VINF_SUCCESS)
920 {
921 STAM_PROFILE_STOP(&pVM->iom.s.StatGCInstLods, a1);
922 iomGCMMIOStatLength(pVM, cbSize);
923 }
924 return rc;
925}
926
927
928/**
929 * CMP [MMIO], reg|imm
930 * CMP reg|imm, [MMIO]
931 *
932 * Restricted implementation.
933 *
934 *
935 * @returns VBox status code.
936 *
937 * @param pVM The virtual machine (GC pointer ofcourse).
938 * @param pRegFrame Trap register frame.
939 * @param GCPhysFault The GC physical address corresponding to pvFault.
940 * @param pCpu Disassembler CPU state.
941 * @param pRange Pointer MMIO range.
942 */
943static int iomGCInterpretCMP(PVM pVM, PCPUMCTXCORE pRegFrame, RTGCPHYS GCPhysFault, PDISCPUSTATE pCpu, CTXALLSUFF(PIOMMMIORANGE) pRange)
944{
945 STAM_PROFILE_START(&pVM->iom.s.StatGCInstCmp, a1);
946
947 /* Check read callback. */
948 if (!pRange->pfnReadCallback)
949 return VINF_EM_RAW_GUEST_TRAP;
950
951 /*
952 * Get the operands.
953 */
954 unsigned cbSize = 0;
955 uint32_t uData1;
956 uint32_t uData2;
957 int rc;
958 if (iomGCGetRegImmData(pCpu, &pCpu->param1, pRegFrame, &uData1, &cbSize))
959 /* cmp reg, [MMIO]. */
960 rc = iomGCMMIODoRead(pVM, pRange, GCPhysFault, &uData2, cbSize);
961 else if (iomGCGetRegImmData(pCpu, &pCpu->param2, pRegFrame, &uData2, &cbSize))
962 /* cmp [MMIO], reg|imm. */
963 rc = iomGCMMIODoRead(pVM, pRange, GCPhysFault, &uData1, cbSize);
964 else
965 {
966 AssertMsgFailed(("Disassember CMP problem..\n"));
967 rc = VERR_IOM_MMIO_HANDLER_DISASM_ERROR;
968 }
969
970 if (rc == VINF_SUCCESS)
971 {
972 /* Emulate CMP and update guest flags. */
973 uint32_t eflags = EMEmulateCmp(uData1, uData2, cbSize);
974 pRegFrame->eflags.u32 = (pRegFrame->eflags.u32 & ~(X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF | X86_EFL_OF))
975 | (eflags & (X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF | X86_EFL_OF));
976
977 STAM_PROFILE_STOP(&pVM->iom.s.StatGCInstCmp, a1);
978 iomGCMMIOStatLength(pVM, cbSize);
979 }
980
981 return rc;
982}
983
984
985/**
986 * AND [MMIO], reg|imm
987 * AND reg, [MMIO]
988 *
989 * Restricted implementation.
990 *
991 *
992 * @returns VBox status code.
993 *
994 * @param pVM The virtual machine (GC pointer ofcourse).
995 * @param pRegFrame Trap register frame.
996 * @param GCPhysFault The GC physical address corresponding to pvFault.
997 * @param pCpu Disassembler CPU state.
998 * @param pRange Pointer MMIO range.
999 */
1000static int iomGCInterpretAND(PVM pVM, PCPUMCTXCORE pRegFrame, RTGCPHYS GCPhysFault, PDISCPUSTATE pCpu, CTXALLSUFF(PIOMMMIORANGE) pRange)
1001{
1002 STAM_PROFILE_START(&pVM->iom.s.StatGCInstAnd, a1);
1003
1004 /* Check read callback. */
1005
1006 unsigned cbSize = 0;
1007 uint32_t uData1;
1008 uint32_t uData2;
1009 bool fAndWrite;
1010 int rc;
1011 if (iomGCGetRegImmData(pCpu, &pCpu->param1, pRegFrame, &uData1, &cbSize))
1012 {
1013 /* and reg, [MMIO]. */
1014 fAndWrite = false;
1015 if (pRange->pfnReadCallback)
1016 rc = iomGCMMIODoRead(pVM, pRange, GCPhysFault, &uData2, cbSize);
1017 else
1018 rc = VINF_IOM_HC_MMIO_READ;
1019 }
1020 else if (iomGCGetRegImmData(pCpu, &pCpu->param2, pRegFrame, &uData2, &cbSize))
1021 {
1022 /* and [MMIO], reg|imm. */
1023 fAndWrite = true;
1024 if (pRange->pfnReadCallback && pRange->pfnWriteCallback)
1025 rc = iomGCMMIODoRead(pVM, pRange, GCPhysFault, &uData1, cbSize);
1026 else
1027 rc = VINF_IOM_HC_MMIO_READ_WRITE;
1028 }
1029 else
1030 {
1031 AssertMsgFailed(("Disassember AND problem..\n"));
1032 return VERR_IOM_MMIO_HANDLER_DISASM_ERROR;
1033 }
1034
1035 if (rc == VINF_SUCCESS)
1036 {
1037 /* Emulate AND and update guest flags. */
1038 uint32_t eflags = EMEmulateAnd(&uData1, uData2, cbSize);
1039 if (fAndWrite)
1040 /* Store result to MMIO. */
1041 rc = iomGCMMIODoWrite(pVM, pRange, GCPhysFault, &uData1, cbSize);
1042 else
1043 {
1044 /* Store result to register. */
1045 bool fRc = iomGCSaveDataToReg(pCpu, &pCpu->param1, pRegFrame, uData1);
1046 AssertMsg(fRc, ("Failed to store register value!\n")); NOREF(fRc);
1047 }
1048 if (rc == VINF_SUCCESS)
1049 {
1050 /* Update guest's eflags and finish. */
1051 pRegFrame->eflags.u32 = (pRegFrame->eflags.u32 & ~(X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF | X86_EFL_OF))
1052 | (eflags & (X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF | X86_EFL_OF));
1053 STAM_PROFILE_STOP(&pVM->iom.s.StatGCInstAnd, a1);
1054 iomGCMMIOStatLength(pVM, cbSize);
1055 }
1056 }
1057
1058 return rc;
1059}
1060
1061
1062
1063/**
1064 * TEST [MMIO], reg|imm
1065 * TEST reg, [MMIO]
1066 *
1067 * Restricted implementation.
1068 *
1069 *
1070 * @returns VBox status code.
1071 *
1072 * @param pVM The virtual machine (GC pointer ofcourse).
1073 * @param pRegFrame Trap register frame.
1074 * @param GCPhysFault The GC physical address corresponding to pvFault.
1075 * @param pCpu Disassembler CPU state.
1076 * @param pRange Pointer MMIO range.
1077 */
1078static int iomGCInterpretTEST(PVM pVM, PCPUMCTXCORE pRegFrame, RTGCPHYS GCPhysFault, PDISCPUSTATE pCpu, CTXALLSUFF(PIOMMMIORANGE) pRange)
1079{
1080 STAM_PROFILE_START(&pVM->iom.s.StatGCInstTest, a1);
1081
1082 /* Check read callback. */
1083
1084 unsigned cbSize = 0;
1085 uint32_t uData1;
1086 uint32_t uData2;
1087 int rc;
1088
1089 if (iomGCGetRegImmData(pCpu, &pCpu->param1, pRegFrame, &uData1, &cbSize))
1090 {
1091 /* and test, [MMIO]. */
1092 if (pRange->pfnReadCallback)
1093 rc = iomGCMMIODoRead(pVM, pRange, GCPhysFault, &uData2, cbSize);
1094 else
1095 rc = VINF_IOM_HC_MMIO_READ;
1096 }
1097 else if (iomGCGetRegImmData(pCpu, &pCpu->param2, pRegFrame, &uData2, &cbSize))
1098 {
1099 /* test [MMIO], reg|imm. */
1100 if (pRange->pfnReadCallback)
1101 rc = iomGCMMIODoRead(pVM, pRange, GCPhysFault, &uData1, cbSize);
1102 else
1103 rc = VINF_IOM_HC_MMIO_READ;
1104 }
1105 else
1106 {
1107 AssertMsgFailed(("Disassember TEST problem..\n"));
1108 return VERR_IOM_MMIO_HANDLER_DISASM_ERROR;
1109 }
1110
1111 if (rc == VINF_SUCCESS)
1112 {
1113 /* Emulate TEST (=AND without write back) and update guest EFLAGS. */
1114 uint32_t eflags = EMEmulateAnd(&uData1, uData2, cbSize);
1115 pRegFrame->eflags.u32 = (pRegFrame->eflags.u32 & ~(X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF | X86_EFL_OF))
1116 | (eflags & (X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF | X86_EFL_SF | X86_EFL_OF));
1117 STAM_PROFILE_STOP(&pVM->iom.s.StatGCInstTest, a1);
1118 iomGCMMIOStatLength(pVM, cbSize);
1119 }
1120
1121 return rc;
1122}
1123
1124/**
1125 * XCHG [MMIO], reg
1126 * XCHG reg, [MMIO]
1127 *
1128 * Restricted implementation.
1129 *
1130 *
1131 * @returns VBox status code.
1132 *
1133 * @param pVM The virtual machine (GC pointer ofcourse).
1134 * @param pRegFrame Trap register frame.
1135 * @param GCPhysFault The GC physical address corresponding to pvFault.
1136 * @param pCpu Disassembler CPU state.
1137 * @param pRange Pointer MMIO range.
1138 */
1139static int iomGCInterpretXCHG(PVM pVM, PCPUMCTXCORE pRegFrame, RTGCPHYS GCPhysFault, PDISCPUSTATE pCpu, CTXALLSUFF(PIOMMMIORANGE) pRange)
1140{
1141 STAM_PROFILE_START(&pVM->iom.s.StatGCInstTest, a1);
1142
1143 /* Check read callback. */
1144 unsigned cbSize = 0;
1145 uint32_t uData1;
1146 uint32_t uData2;
1147 int rc;
1148
1149 if (!pRange->pfnReadCallback || !pRange->pfnWriteCallback)
1150 {
1151 rc = VINF_IOM_HC_MMIO_READ;
1152 goto end;
1153 }
1154
1155 if (iomGCGetRegImmData(pCpu, &pCpu->param1, pRegFrame, &uData1, &cbSize))
1156 {
1157 /* xchg reg, [MMIO]. */
1158 rc = iomGCMMIODoRead(pVM, pRange, GCPhysFault, &uData2, cbSize);
1159 if (rc == VINF_SUCCESS)
1160 {
1161 /* Store result to MMIO. */
1162 rc = iomGCMMIODoWrite(pVM, pRange, GCPhysFault, &uData1, cbSize);
1163
1164 if (rc == VINF_SUCCESS)
1165 {
1166 /* Store result to register. */
1167 bool fRc = iomGCSaveDataToReg(pCpu, &pCpu->param1, pRegFrame, uData2);
1168 AssertMsg(fRc, ("Failed to store register value!\n")); NOREF(fRc);
1169 }
1170 else
1171 Assert(rc == VINF_IOM_HC_MMIO_WRITE || rc == VINF_PATM_HC_MMIO_PATCH_WRITE);
1172 }
1173 else
1174 Assert(rc == VINF_IOM_HC_MMIO_READ || rc == VINF_PATM_HC_MMIO_PATCH_READ);
1175 }
1176 else
1177 if (iomGCGetRegImmData(pCpu, &pCpu->param2, pRegFrame, &uData2, &cbSize))
1178 {
1179 /* xchg [MMIO], reg. */
1180 rc = iomGCMMIODoRead(pVM, pRange, GCPhysFault, &uData1, cbSize);
1181 if (rc == VINF_SUCCESS)
1182 {
1183 /* Store result to MMIO. */
1184 rc = iomGCMMIODoWrite(pVM, pRange, GCPhysFault, &uData2, cbSize);
1185
1186 if (rc == VINF_SUCCESS)
1187 {
1188 /* Store result to register. */
1189 bool fRc = iomGCSaveDataToReg(pCpu, &pCpu->param2, pRegFrame, uData1);
1190 AssertMsg(fRc, ("Failed to store register value!\n")); NOREF(fRc);
1191 }
1192 else
1193 Assert(rc == VINF_IOM_HC_MMIO_WRITE || rc == VINF_PATM_HC_MMIO_PATCH_WRITE);
1194 }
1195 else
1196 Assert(rc == VINF_IOM_HC_MMIO_READ || rc == VINF_PATM_HC_MMIO_PATCH_READ);
1197 }
1198 else
1199 {
1200 AssertMsgFailed(("Disassember XCHG problem..\n"));
1201 rc = VERR_IOM_MMIO_HANDLER_DISASM_ERROR;
1202 goto end;
1203 }
1204
1205end:
1206 STAM_PROFILE_STOP(&pVM->iom.s.StatGCInstTest, a1);
1207 return rc;
1208}
1209
1210
1211#ifdef IN_RING0
1212
1213/**
1214 * Read callback for disassembly function; supports reading bytes that cross a page boundary
1215 *
1216 * @returns VBox status code.
1217 * @param pSrc GC source pointer
1218 * @param pDest HC destination pointer
1219 * @param size Number of bytes to read
1220 * @param dwUserdata Callback specific user data (pCpu)
1221 *
1222 */
1223DECLCALLBACK(int32_t) iomReadBytes(RTHCUINTPTR pSrc, uint8_t *pDest, uint32_t size, RTHCUINTPTR dwUserdata)
1224{
1225 DISCPUSTATE *pCpu = (DISCPUSTATE *)dwUserdata;
1226 PVM pVM = (PVM)pCpu->dwUserData[0];
1227
1228 int rc = PGMPhysReadGCPtr(pVM, pDest, pSrc, size);
1229 AssertRC(rc);
1230 return rc;
1231}
1232
1233inline bool iomDisCoreOne(PVM pVM, DISCPUSTATE *pCpu, RTGCUINTPTR InstrGC, uint32_t *pOpsize)
1234{
1235 return VBOX_SUCCESS(DISCoreOneEx(InstrGC, pCpu->mode, iomReadBytes, pVM, pCpu, pOpsize));
1236}
1237#else
1238inline bool iomDisCoreOne(PVM pVM, DISCPUSTATE *pCpu, RTGCUINTPTR InstrGC, uint32_t *pOpsize)
1239{
1240 return DISCoreOne(pCpu, InstrGC, pOpsize);
1241}
1242
1243#endif
1244
1245/**
1246 * \#PF Handler callback for MMIO ranges.
1247 * Note: we are on ring0 in Hypervisor and interrupts are disabled.
1248 *
1249 * @returns VBox status code (appropriate for GC return).
1250 * @param pVM VM Handle.
1251 * @param uErrorCode CPU Error code.
1252 * @param pRegFrame Trap register frame.
1253 * @param pvFault The fault address (cr2).
1254 * @param GCPhysFault The GC physical address corresponding to pvFault.
1255 * @param pvUser Pointer to the MMIO ring-3 range entry.
1256 */
1257IOMDECL(int) IOMMMIOHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, void *pvFault, RTGCPHYS GCPhysFault, void *pvUser)
1258{
1259 STAM_PROFILE_START(&pVM->iom.s.StatGCMMIOHandler, a);
1260 NOREF(pvUser); /** @todo implement pvUser usage! */
1261 Log3(("IOMMMIOHandler: GCPhys=%RGp uErr=%#x pvFault=%p eip=%RGv\n",
1262 GCPhysFault, uErrorCode, pvFault, pRegFrame->eip));
1263
1264 /*
1265 * Find the corresponding MMIO range.
1266 */
1267 CTXALLSUFF(PIOMMMIORANGE) pRange = iomMMIOGetRange(&pVM->iom.s, GCPhysFault);
1268 if (!pRange)
1269 {
1270#ifdef VBOX_WITH_STATISTICS
1271 PIOMMMIOSTATS pStats = iomMMIOGetStats(&pVM->iom.s, GCPhysFault);
1272 if (pStats)
1273 {
1274 if (uErrorCode & X86_TRAP_PF_RW)
1275 STAM_COUNTER_INC(&pStats->WriteGCToR3);
1276 else
1277 STAM_COUNTER_INC(&pStats->ReadGCToR3);
1278 }
1279#endif
1280 PIOMMMIORANGER3 pRangeR3 = iomMMIOGetRangeHC(&pVM->iom.s, GCPhysFault);
1281 if (pRangeR3)
1282 {
1283 STAM_PROFILE_START(&pVM->iom.s.StatGCMMIOHandler, a);
1284 STAM_COUNTER_INC(&pVM->iom.s.StatGCMMIOFailures);
1285 return (uErrorCode & X86_TRAP_PF_RW) ? VINF_IOM_HC_MMIO_WRITE : VINF_IOM_HC_MMIO_READ;
1286 }
1287
1288 /*
1289 * Now, why are we here...
1290 */
1291 AssertMsgFailed(("Internal error! GCPhysFault=%x\n", GCPhysFault));
1292 return VERR_IOM_MMIO_HANDLER_BOGUS_CALL;
1293 }
1294
1295 /*
1296 * Convert CS:EIP to linear address and initialize the disassembler.
1297 */
1298 DISCPUSTATE cpu;
1299 cpu.mode = SELMIsSelector32Bit(pVM, pRegFrame->eflags, pRegFrame->cs, &pRegFrame->csHid) ? CPUMODE_32BIT : CPUMODE_16BIT;
1300
1301 RTGCPTR pvCode;
1302 int rc = SELMValidateAndConvertCSAddr(pVM, pRegFrame->eflags, pRegFrame->ss, pRegFrame->cs, &pRegFrame->csHid, (RTGCPTR)(cpu.mode == CPUMODE_32BIT ? pRegFrame->eip : pRegFrame->eip & 0xffff), &pvCode);
1303 if (VBOX_FAILURE(rc))
1304 {
1305 AssertMsgFailed(("Internal error! cs:eip=%04x:%08x\n", pRegFrame->cs, pRegFrame->eip));
1306 return VERR_IOM_MMIO_HANDLER_BOGUS_CALL;
1307 }
1308
1309 /*
1310 * Disassemble the instruction and interprete it.
1311 */
1312 unsigned cbOp;
1313 if (iomDisCoreOne(pVM, &cpu, (RTGCUINTPTR)pvCode, &cbOp))
1314 {
1315 switch (cpu.pCurInstr->opcode)
1316 {
1317 case OP_MOV:
1318 case OP_MOVZX:
1319 case OP_MOVSX:
1320 {
1321 STAM_PROFILE_START(&pVM->iom.s.StatGCInstMov, b);
1322 if (uErrorCode & X86_TRAP_PF_RW)
1323 rc = iomGCInterpretMOVxXWrite(pVM, pRegFrame, &cpu, pRange, GCPhysFault);
1324 else
1325 rc = iomGCInterpretMOVxXRead(pVM, pRegFrame, &cpu, pRange, GCPhysFault);
1326 if (rc == VINF_SUCCESS)
1327 STAM_PROFILE_STOP(&pVM->iom.s.StatGCInstMov, b);
1328 break;
1329 }
1330
1331
1332#ifdef IOMGC_MOVS_SUPPORT
1333 case OP_MOVSB:
1334 case OP_MOVSWD:
1335 rc = iomGCInterpretMOVS(pVM, uErrorCode, pRegFrame, GCPhysFault, &cpu, pRange);
1336 break;
1337#endif
1338
1339 case OP_STOSB:
1340 case OP_STOSWD:
1341 Assert(uErrorCode & X86_TRAP_PF_RW);
1342 rc = iomGCInterpretSTOS(pVM, pRegFrame, GCPhysFault, &cpu, pRange);
1343 break;
1344
1345 case OP_LODSB:
1346 case OP_LODSWD:
1347 Assert(!(uErrorCode & X86_TRAP_PF_RW));
1348 rc = iomGCInterpretLODS(pVM, pRegFrame, GCPhysFault, &cpu, pRange);
1349 break;
1350
1351
1352 case OP_CMP:
1353 Assert(!(uErrorCode & X86_TRAP_PF_RW));
1354 rc = iomGCInterpretCMP(pVM, pRegFrame, GCPhysFault, &cpu, pRange);
1355 break;
1356
1357 case OP_AND:
1358 rc = iomGCInterpretAND(pVM, pRegFrame, GCPhysFault, &cpu, pRange);
1359 break;
1360
1361 case OP_TEST:
1362 Assert(!(uErrorCode & X86_TRAP_PF_RW));
1363 rc = iomGCInterpretTEST(pVM, pRegFrame, GCPhysFault, &cpu, pRange);
1364 break;
1365
1366 case OP_XCHG:
1367 rc = iomGCInterpretXCHG(pVM, pRegFrame, GCPhysFault, &cpu, pRange);
1368 break;
1369
1370
1371 /*
1372 * The instruction isn't supported. Hand it on to ring-3.
1373 */
1374 default:
1375 STAM_COUNTER_INC(&pVM->iom.s.StatGCInstOther);
1376 rc = (uErrorCode & X86_TRAP_PF_RW) ? VINF_IOM_HC_MMIO_WRITE : VINF_IOM_HC_MMIO_READ;
1377 break;
1378 }
1379 }
1380 else
1381 {
1382 AssertMsgFailed(("Disassembler freaked out!\n"));
1383 rc = VERR_IOM_MMIO_HANDLER_DISASM_ERROR;
1384 }
1385
1386 /*
1387 * On success advance EIP.
1388 */
1389 if (rc == VINF_SUCCESS)
1390 pRegFrame->eip += cbOp;
1391 else
1392 {
1393 STAM_COUNTER_INC(&pVM->iom.s.StatGCMMIOFailures);
1394#ifdef VBOX_WITH_STATISTICS
1395 switch (rc)
1396 {
1397 case VINF_IOM_HC_MMIO_READ:
1398 case VINF_IOM_HC_MMIO_WRITE:
1399 case VINF_IOM_HC_MMIO_READ_WRITE:
1400 {
1401 PIOMMMIOSTATS pStats = iomMMIOGetStats(&pVM->iom.s, GCPhysFault);
1402 if (pStats)
1403 {
1404 if (uErrorCode & X86_TRAP_PF_RW)
1405 STAM_COUNTER_INC(&pStats->WriteGCToR3);
1406 else
1407 STAM_COUNTER_INC(&pStats->ReadGCToR3);
1408 }
1409 }
1410 break;
1411 }
1412#endif
1413 }
1414 STAM_PROFILE_STOP(&pVM->iom.s.StatGCMMIOHandler, a);
1415 return rc;
1416}
1417
1418
1419#endif /* !IN_RING3 */
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