VirtualBox

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

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

svn:eol-style native

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