VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMGC/IOMGC.cpp@ 1108

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

string.h & stdio.h + header cleanups.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 24.0 KB
Line 
1/* $Id: IOMGC.cpp 23 2007-01-15 14:08:28Z 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/** @def IOMGC_MOVS_SUPPORT
49 * Define IOMGC_MOVS_SUPPORT for movsb/w/d support in GC.
50 */
51#define IOMGC_MOVS_SUPPORT
52
53
54/*******************************************************************************
55* Internal Functions *
56*******************************************************************************/
57#if 0
58static bool iomGCCalcParamEA(PDISCPUSTATE pCpu, POP_PARAMETER pParam, PCPUMCTXCORE pRegFrame, void **ppAddr);
59#endif
60static unsigned iomGCGetRegSize(PDISCPUSTATE pCpu, PCOP_PARAMETER pParam);
61static bool iomGCGetRegImmData(PDISCPUSTATE pCpu, PCOP_PARAMETER pParam, PCPUMCTXCORE pRegFrame, uint32_t *pu32Data, unsigned *pcbSize);
62static bool iomGCSaveDataToReg(PDISCPUSTATE pCpu, PCOP_PARAMETER pParam, PCPUMCTXCORE pRegFrame, uint32_t u32Data);
63
64
65/*******************************************************************************
66* Global Variables *
67*******************************************************************************/
68/**
69 * Array for accessing 32-bit general registers in VMMREGFRAME structure
70 * by register's index from disasm.
71 */
72static unsigned g_aReg32Index[] =
73{
74 RT_OFFSETOF(CPUMCTXCORE, eax), /* USE_REG_EAX */
75 RT_OFFSETOF(CPUMCTXCORE, ecx), /* USE_REG_ECX */
76 RT_OFFSETOF(CPUMCTXCORE, edx), /* USE_REG_EDX */
77 RT_OFFSETOF(CPUMCTXCORE, ebx), /* USE_REG_EBX */
78 RT_OFFSETOF(CPUMCTXCORE, esp), /* USE_REG_ESP */
79 RT_OFFSETOF(CPUMCTXCORE, ebp), /* USE_REG_EBP */
80 RT_OFFSETOF(CPUMCTXCORE, esi), /* USE_REG_ESI */
81 RT_OFFSETOF(CPUMCTXCORE, edi) /* USE_REG_EDI */
82};
83
84/**
85 * Macro for accessing 32-bit general purpose registers in CPUMCTXCORE structure.
86 */
87#define ACCESS_REG32(p, idx) (*((uint32_t *)((char *)(p) + g_aReg32Index[idx])))
88
89/**
90 * Array for accessing 16-bit general registers in CPUMCTXCORE structure
91 * by register's index from disasm.
92 */
93static unsigned g_aReg16Index[] =
94{
95 RT_OFFSETOF(CPUMCTXCORE, eax), /* USE_REG_AX */
96 RT_OFFSETOF(CPUMCTXCORE, ecx), /* USE_REG_CX */
97 RT_OFFSETOF(CPUMCTXCORE, edx), /* USE_REG_DX */
98 RT_OFFSETOF(CPUMCTXCORE, ebx), /* USE_REG_BX */
99 RT_OFFSETOF(CPUMCTXCORE, esp), /* USE_REG_SP */
100 RT_OFFSETOF(CPUMCTXCORE, ebp), /* USE_REG_BP */
101 RT_OFFSETOF(CPUMCTXCORE, esi), /* USE_REG_SI */
102 RT_OFFSETOF(CPUMCTXCORE, edi) /* USE_REG_DI */
103};
104
105/**
106 * Macro for accessing 16-bit general purpose registers in CPUMCTXCORE structure.
107 */
108#define ACCESS_REG16(p, idx) (*((uint16_t *)((char *)(p) + g_aReg16Index[idx])))
109
110/**
111 * Array for accessing 8-bit general registers in CPUMCTXCORE structure
112 * by register's index from disasm.
113 */
114static unsigned g_aReg8Index[] =
115{
116 RT_OFFSETOF(CPUMCTXCORE, eax), /* USE_REG_AL */
117 RT_OFFSETOF(CPUMCTXCORE, ecx), /* USE_REG_CL */
118 RT_OFFSETOF(CPUMCTXCORE, edx), /* USE_REG_DL */
119 RT_OFFSETOF(CPUMCTXCORE, ebx), /* USE_REG_BL */
120 RT_OFFSETOF(CPUMCTXCORE, eax) + 1, /* USE_REG_AH */
121 RT_OFFSETOF(CPUMCTXCORE, ecx) + 1, /* USE_REG_CH */
122 RT_OFFSETOF(CPUMCTXCORE, edx) + 1, /* USE_REG_DH */
123 RT_OFFSETOF(CPUMCTXCORE, ebx) + 1 /* USE_REG_BH */
124};
125
126/**
127 * Macro for accessing 8-bit general purpose registers in CPUMCTXCORE structure.
128 */
129#define ACCESS_REG8(p, idx) (*((uint8_t *)((char *)(p) + g_aReg8Index[idx])))
130
131/**
132 * Array for accessing segment registers in CPUMCTXCORE structure
133 * by register's index from disasm.
134 */
135static unsigned g_aRegSegIndex[] =
136{
137 RT_OFFSETOF(CPUMCTXCORE, es), /* USE_REG_ES */
138 RT_OFFSETOF(CPUMCTXCORE, cs), /* USE_REG_CS */
139 RT_OFFSETOF(CPUMCTXCORE, ss), /* USE_REG_SS */
140 RT_OFFSETOF(CPUMCTXCORE, ds), /* USE_REG_DS */
141 RT_OFFSETOF(CPUMCTXCORE, fs), /* USE_REG_FS */
142 RT_OFFSETOF(CPUMCTXCORE, gs) /* USE_REG_GS */
143};
144
145/**
146 * Macro for accessing segment registers in CPUMCTXCORE structure.
147 */
148#define ACCESS_REGSEG(p, idx) (*((uint16_t *)((char *)(p) + g_aRegSegIndex[idx])))
149
150/**
151 * Array for fast recode of the operand size (1/2/4/8 bytes) to bit shift value.
152 */
153static const unsigned g_aSize2Shift[] =
154{
155 ~0, /* 0 - invalid */
156 0, /* *1 == 2^0 */
157 1, /* *2 == 2^1 */
158 ~0, /* 3 - invalid */
159 2, /* *4 == 2^2 */
160 ~0, /* 5 - invalid */
161 ~0, /* 6 - invalid */
162 ~0, /* 7 - invalid */
163 3 /* *8 == 2^3 */
164};
165
166/**
167 * Macro for fast recode of the operand size (1/2/4/8 bytes) to bit shift value.
168 */
169#define SIZE2SHIFT(cb) (g_aSize2Shift[cb])
170
171
172#if 0
173/**
174 * Calculates effective address (offset from current segment register) for
175 * instruction parameter, i.e. [eax + esi*4 + 1234h] -> virtual address.
176 *
177 * @returns true on success.
178 * @param pCpu Pointer to current disassembler context.
179 * @param pParam Pointer to parameter of instruction to calc EA.
180 * @param pRegFrame Pointer to CPUMCTXCORE guest structure.
181 * @param ppAddr Where to store result address.
182 */
183static bool iomGCCalcParamEA(PDISCPUSTATE pCpu, POP_PARAMETER pParam, PCPUMCTXCORE pRegFrame, void **ppAddr)
184{
185 uint8_t *pAddr = 0;
186
187 if (pCpu->addrmode == CPUMODE_32BIT)
188 {
189 /* 32-bit addressing. */
190 if (pParam->flags & USE_BASE)
191 pAddr += ACCESS_REG32(pRegFrame, pParam->base.reg_gen32);
192 if (pParam->flags & USE_INDEX)
193 {
194 unsigned i = ACCESS_REG32(pRegFrame, pParam->index.reg_gen);
195 if (pParam->flags & USE_SCALE)
196 i *= pParam->scale;
197 pAddr += i;
198 }
199 if (pParam->flags & USE_DISPLACEMENT8)
200 pAddr += pParam->disp8;
201 else
202 if (pParam->flags & USE_DISPLACEMENT16)
203 pAddr += pParam->disp16;
204 else
205 if (pParam->flags & USE_DISPLACEMENT32)
206 pAddr += pParam->disp32;
207
208 if (pParam->flags & (USE_BASE | USE_INDEX | USE_DISPLACEMENT8 | USE_DISPLACEMENT16 | USE_DISPLACEMENT32))
209 {
210 /* EA present in parameter. */
211 *ppAddr = pAddr;
212 return true;
213 }
214 }
215 else
216 {
217 /* 16-bit addressing. */
218 if (pParam->flags & USE_BASE)
219 pAddr += ACCESS_REG16(pRegFrame, pParam->base.reg_gen16);
220 if (pParam->flags & USE_INDEX)
221 pAddr += ACCESS_REG16(pRegFrame, pParam->index.reg_gen);
222 if (pParam->flags & USE_DISPLACEMENT8)
223 pAddr += pParam->disp8;
224 else
225 if (pParam->flags & USE_DISPLACEMENT16)
226 pAddr += pParam->disp16;
227
228 if (pParam->flags & (USE_BASE | USE_INDEX | USE_DISPLACEMENT8 | USE_DISPLACEMENT16))
229 {
230 /* EA present in parameter. */
231 *ppAddr = pAddr;
232 return true;
233 }
234 }
235
236 /* Error exit. */
237 return false;
238}
239#endif
240
241/**
242 * Calculates the size of register parameter.
243 *
244 * @returns 1, 2, 4 on success.
245 * @returns 0 if non-register parameter.
246 * @param pCpu Pointer to current disassembler context.
247 * @param pParam Pointer to parameter of instruction to proccess.
248 */
249static unsigned iomGCGetRegSize(PDISCPUSTATE pCpu, PCOP_PARAMETER pParam)
250{
251 if (pParam->flags & (USE_BASE | USE_INDEX | USE_SCALE | USE_DISPLACEMENT8 | USE_DISPLACEMENT16 | USE_DISPLACEMENT32 | USE_IMMEDIATE8 | USE_IMMEDIATE16 | USE_IMMEDIATE32 | USE_IMMEDIATE16_SX8 | USE_IMMEDIATE32_SX8))
252 return 0;
253
254 if (pParam->flags & USE_REG_GEN32)
255 return 4;
256
257 if (pParam->flags & USE_REG_GEN16)
258 return 2;
259
260 if (pParam->flags & USE_REG_GEN8)
261 return 1;
262
263 if (pParam->flags & USE_REG_SEG)
264 return 2;
265 return 0;
266}
267
268/**
269 * Returns the contents of register or immediate data of instruction's parameter.
270 *
271 * @returns true on success.
272 *
273 * @param pCpu Pointer to current disassembler context.
274 * @param pParam Pointer to parameter of instruction to proccess.
275 * @param pRegFrame Pointer to CPUMCTXCORE guest structure.
276 * @param pu32Data Where to store retrieved data.
277 * @param pcbSize Where to store the size of data (1, 2, 4).
278 */
279static bool iomGCGetRegImmData(PDISCPUSTATE pCpu, PCOP_PARAMETER pParam, PCPUMCTXCORE pRegFrame, uint32_t *pu32Data, unsigned *pcbSize)
280{
281 if (pParam->flags & (USE_BASE | USE_INDEX | USE_SCALE | USE_DISPLACEMENT8 | USE_DISPLACEMENT16 | USE_DISPLACEMENT32))
282 {
283 *pcbSize = 0;
284 *pu32Data = 0;
285 return false;
286 }
287
288 if (pParam->flags & USE_REG_GEN32)
289 {
290 *pcbSize = 4;
291 *pu32Data = ACCESS_REG32(pRegFrame, pParam->base.reg_gen32);
292 return true;
293 }
294
295 if (pParam->flags & USE_REG_GEN16)
296 {
297 *pcbSize = 2;
298 *pu32Data = ACCESS_REG16(pRegFrame, pParam->base.reg_gen16);
299 return true;
300 }
301
302 if (pParam->flags & USE_REG_GEN8)
303 {
304 *pcbSize = 1;
305 *pu32Data = ACCESS_REG8(pRegFrame, pParam->base.reg_gen8);
306 return true;
307 }
308
309 if (pParam->flags & (USE_IMMEDIATE32|USE_IMMEDIATE32_SX8))
310 {
311 *pcbSize = 4;
312 *pu32Data = (uint32_t)pParam->parval;
313 return true;
314 }
315
316 if (pParam->flags & (USE_IMMEDIATE16|USE_IMMEDIATE16_SX8))
317 {
318 *pcbSize = 2;
319 *pu32Data = (uint16_t)pParam->parval;
320 return true;
321 }
322
323 if (pParam->flags & USE_IMMEDIATE8)
324 {
325 *pcbSize = 1;
326 *pu32Data = (uint8_t)pParam->parval;
327 return true;
328 }
329
330 if (pParam->flags & USE_REG_SEG)
331 {
332 *pcbSize = 2;
333 *pu32Data = ACCESS_REGSEG(pRegFrame, pParam->base.reg_seg);
334 return true;
335 } /* Else - error. */
336
337 *pcbSize = 0;
338 *pu32Data = 0;
339 return false;
340}
341
342
343/**
344 * Saves data to 8/16/32 general purpose or segment register defined by
345 * instruction's parameter.
346 *
347 * @returns true on success.
348 * @param pCpu Pointer to current disassembler context.
349 * @param pParam Pointer to parameter of instruction to proccess.
350 * @param pRegFrame Pointer to CPUMCTXCORE guest structure.
351 * @param u32Data 8/16/32 bit data to store.
352 */
353static bool iomGCSaveDataToReg(PDISCPUSTATE pCpu, PCOP_PARAMETER pParam, PCPUMCTXCORE pRegFrame, unsigned u32Data)
354{
355 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))
356 {
357 return false;
358 }
359
360 if (pParam->flags & USE_REG_GEN32)
361 {
362 ACCESS_REG32(pRegFrame, pParam->base.reg_gen32) = u32Data;
363 return true;
364 }
365
366 if (pParam->flags & USE_REG_GEN16)
367 {
368 ACCESS_REG16(pRegFrame, pParam->base.reg_gen16) = (uint16_t)u32Data;
369 return true;
370 }
371
372 if (pParam->flags & USE_REG_GEN8)
373 {
374 ACCESS_REG8(pRegFrame, pParam->base.reg_gen8) = (uint8_t)u32Data;
375 return true;
376 }
377
378 if (pParam->flags & USE_REG_SEG)
379 {
380 ACCESS_REGSEG(pRegFrame, pParam->base.reg_seg) = (uint16_t)u32Data;
381 return true;
382 }
383
384 /* Else - error. */
385 return false;
386}
387
388
389/*
390 * Internal - statistics only.
391 */
392inline void iomGCMMIOStatLength(PVM pVM, unsigned cb)
393{
394#ifdef VBOX_WITH_STATISTICS
395 switch (cb)
396 {
397 case 1:
398 STAM_COUNTER_INC(&pVM->iom.s.StatGCMMIO1Byte);
399 break;
400 case 2:
401 STAM_COUNTER_INC(&pVM->iom.s.StatGCMMIO2Bytes);
402 break;
403 case 4:
404 STAM_COUNTER_INC(&pVM->iom.s.StatGCMMIO4Bytes);
405 break;
406 default:
407 /* No way. */
408 AssertMsgFailed(("Invalid data length %d\n", cb));
409 break;
410 }
411#else
412 NOREF(pVM); NOREF(cb);
413#endif
414}
415
416
417/**
418 * IN <AL|AX|EAX>, <DX|imm16>
419 *
420 * @returns VBox status code.
421 *
422 * @param pVM The virtual machine (GC pointer ofcourse).
423 * @param pRegFrame Pointer to CPUMCTXCORE guest registers structure.
424 * @param pCpu Disassembler CPU state.
425 */
426IOMDECL(int) IOMInterpretIN(PVM pVM, PCPUMCTXCORE pRegFrame, PDISCPUSTATE pCpu)
427{
428#ifdef VBOX_WITH_STATISTICS
429 STAM_COUNTER_INC(&pVM->iom.s.StatGCInstIn);
430#endif
431
432 /*
433 * Get port number from second parameter.
434 * And get the register size from the first parameter.
435 */
436 uint32_t uPort = 0;
437 unsigned cbSize = 0;
438 bool fRc = iomGCGetRegImmData(pCpu, &pCpu->param2, pRegFrame, &uPort, &cbSize);
439 AssertMsg(fRc, ("Failed to get reg/imm port number!\n")); NOREF(fRc);
440
441 cbSize = iomGCGetRegSize(pCpu, &pCpu->param1);
442 Assert(cbSize > 0);
443 int rc = IOMInterpretCheckPortIOAccess(pVM, pRegFrame, uPort, cbSize);
444 if (rc == VINF_SUCCESS)
445 {
446 /*
447 * Attemp to read the port.
448 */
449 uint32_t u32Data = ~0U;
450 rc = IOMIOPortRead(pVM, uPort, &u32Data, cbSize);
451 if (rc == VINF_SUCCESS)
452 {
453 /*
454 * Store the result in the AL|AX|EAX register.
455 */
456 fRc = iomGCSaveDataToReg(pCpu, &pCpu->param1, pRegFrame, u32Data);
457 AssertMsg(fRc, ("Failed to store register value!\n")); NOREF(fRc);
458 }
459 }
460 return rc;
461}
462
463
464/**
465 * OUT <DX|imm16>, <AL|AX|EAX>
466 *
467 * @returns VBox status code.
468 *
469 * @param pVM The virtual machine (GC pointer ofcourse).
470 * @param pRegFrame Pointer to CPUMCTXCORE guest registers structure.
471 * @param pCpu Disassembler CPU state.
472 */
473IOMDECL(int) IOMInterpretOUT(PVM pVM, PCPUMCTXCORE pRegFrame, PDISCPUSTATE pCpu)
474{
475#ifdef VBOX_WITH_STATISTICS
476 STAM_COUNTER_INC(&pVM->iom.s.StatGCInstOut);
477#endif
478
479 /*
480 * Get port number from first parameter.
481 * And get the register size and value from the second parameter.
482 */
483 uint32_t uPort = 0;
484 unsigned cbSize = 0;
485 bool fRc = iomGCGetRegImmData(pCpu, &pCpu->param1, pRegFrame, &uPort, &cbSize);
486 AssertMsg(fRc, ("Failed to get reg/imm port number!\n")); NOREF(fRc);
487
488 int rc = IOMInterpretCheckPortIOAccess(pVM, pRegFrame, uPort, cbSize);
489 if (rc == VINF_SUCCESS)
490 {
491 uint32_t u32Data = 0;
492 fRc = iomGCGetRegImmData(pCpu, &pCpu->param2, pRegFrame, &u32Data, &cbSize);
493 AssertMsg(fRc, ("Failed to get reg value!\n")); NOREF(fRc);
494
495 /*
496 * Attemp to write to the port.
497 */
498 rc = IOMIOPortWrite(pVM, uPort, u32Data, cbSize);
499 }
500 return rc;
501}
502
503
504/**
505 * [REP*] INSB/INSW/INSD
506 * ES:EDI,DX[,ECX]
507 *
508 * @returns VBox status code.
509 *
510 * @param pVM The virtual machine (GC pointer ofcourse).
511 * @param pRegFrame Pointer to CPUMCTXCORE guest registers structure.
512 * @param pCpu Disassembler CPU state.
513 */
514IOMDECL(int) IOMInterpretINS(PVM pVM, PCPUMCTXCORE pRegFrame, PDISCPUSTATE pCpu)
515{
516#ifdef VBOX_WITH_STATISTICS
517 STAM_COUNTER_INC(&pVM->iom.s.StatGCInstIns);
518#endif
519
520 /*
521 * We do not support REPNE, 16-bit addressing or decrementing destination
522 * pointer. Segment prefixes are deliberately ignored, as per the instruction specification.
523 */
524 if ( pCpu->prefix & PREFIX_REPNE
525 || (pCpu->addrmode != CPUMODE_32BIT)
526 || pRegFrame->eflags.Bits.u1DF)
527 return VINF_IOM_HC_IOPORT_READ;
528
529 /*
530 * Get port number directly from the register (no need to bother the
531 * disassembler). And get the I/O register size from the opcode / prefix.
532 */
533 uint32_t uPort = pRegFrame->edx & 0xffff;
534 unsigned cbSize = 0;
535 if (pCpu->pCurInstr->opcode == OP_INSB)
536 cbSize = 1;
537 else
538 cbSize = pCpu->opmode == CPUMODE_32BIT ? 4 : 2;
539
540 int rc = IOMInterpretCheckPortIOAccess(pVM, pRegFrame, uPort, cbSize);
541 if (rc == VINF_SUCCESS)
542 {
543 /*
544 * Get bytes/words/dwords count to transfer.
545 */
546 RTGCUINTREG cTransfers = 1;
547 if (pCpu->prefix & PREFIX_REP)
548 {
549 cTransfers = pRegFrame->ecx;
550 if (!cTransfers)
551 return VINF_SUCCESS;
552 }
553
554 /* Convert destination address es:edi. */
555 RTGCPTR GCPtrDst;
556 rc = SELMToFlatEx(pVM, pRegFrame->es, (RTGCPTR)pRegFrame->edi,
557 SELMTOFLAT_FLAGS_HYPER | SELMTOFLAT_FLAGS_NO_PL,
558 &GCPtrDst, NULL);
559 if (VBOX_FAILURE(rc))
560 {
561 Log(("INS destination address conversion failed -> fallback, rc=%d\n", rc));
562 return VINF_IOM_HC_IOPORT_READ;
563 }
564
565 /* Access verification first; we can't recover from traps inside this instruction, as the port read cannot be repeated. */
566 rc = PGMVerifyAccess(pVM, (RTGCUINTPTR)GCPtrDst, cTransfers * cbSize,
567 X86_PTE_RW | (((pRegFrame->ss & X86_SEL_RPL) == 3) ? X86_PTE_US : 0));
568 if (rc != VINF_SUCCESS)
569 {
570 Log(("INS will generate a trap -> fallback, rc=%d\n", rc));
571 return VINF_IOM_HC_IOPORT_READ;
572 }
573
574 Log(("IOMGC: rep ins%d port %#x count %d\n", cbSize * 8, uPort, cTransfers));
575 MMGCRamRegisterTrapHandler(pVM);
576
577 /* If the device supports string transfers, ask it to do as
578 * much as it wants. The rest is done with single-word transfers. */
579 const RTGCUINTREG cTransfersOrg = cTransfers;
580 rc = IOMIOPortReadString(pVM, uPort, &GCPtrDst, &cTransfers, cbSize);
581 AssertRC(rc); Assert(cTransfers <= cTransfersOrg);
582 pRegFrame->edi += (cTransfersOrg - cTransfers) * cbSize;
583
584 while (cTransfers && rc == VINF_SUCCESS)
585 {
586 uint32_t u32Value;
587 rc = IOMIOPortRead(pVM, uPort, &u32Value, cbSize);
588 if (rc == VINF_IOM_HC_IOPORT_READ || VBOX_FAILURE(rc))
589 break;
590 int rc2 = MMGCRamWriteNoTrapHandler(GCPtrDst, &u32Value, cbSize);
591 Assert(rc2 == VINF_SUCCESS); NOREF(rc2);
592 GCPtrDst = (RTGCPTR)((RTGCUINTPTR)GCPtrDst + cbSize);
593 pRegFrame->edi += cbSize;
594 cTransfers--;
595 }
596 MMGCRamDeregisterTrapHandler(pVM);
597
598 /* Update ecx on exit. */
599 if (pCpu->prefix & PREFIX_REP)
600 pRegFrame->ecx = cTransfers;
601 }
602 return rc;
603}
604
605
606/**
607 * [REP*] OUTSB/OUTSW/OUTSD
608 * DS:ESI,DX[,ECX]
609 *
610 * @returns VBox status code.
611 *
612 * @param pVM The virtual machine (GC pointer ofcourse).
613 * @param pRegFrame Pointer to CPUMCTXCORE guest registers structure.
614 * @param pCpu Disassembler CPU state.
615 */
616IOMDECL(int) IOMInterpretOUTS(PVM pVM, PCPUMCTXCORE pRegFrame, PDISCPUSTATE pCpu)
617{
618#ifdef VBOX_WITH_STATISTICS
619 STAM_COUNTER_INC(&pVM->iom.s.StatGCInstOuts);
620#endif
621
622 /*
623 * We do not support segment prefixes, REPNE, 16-bit addressing or
624 * decrementing source pointer.
625 */
626 if ( pCpu->prefix & (PREFIX_SEG | PREFIX_REPNE)
627 || (pCpu->addrmode != CPUMODE_32BIT)
628 || pRegFrame->eflags.Bits.u1DF)
629 return VINF_IOM_HC_IOPORT_WRITE;
630
631 /*
632 * Get port number from the first parameter.
633 * And get the I/O register size from the opcode / prefix.
634 */
635 uint32_t uPort = 0;
636 unsigned cbSize = 0;
637 bool fRc = iomGCGetRegImmData(pCpu, &pCpu->param1, pRegFrame, &uPort, &cbSize);
638 AssertMsg(fRc, ("Failed to get reg/imm port number!\n")); NOREF(fRc);
639 if (pCpu->pCurInstr->opcode == OP_OUTSB)
640 cbSize = 1;
641 else
642 cbSize = pCpu->opmode == CPUMODE_32BIT ? 4 : 2;
643
644 int rc = IOMInterpretCheckPortIOAccess(pVM, pRegFrame, uPort, cbSize);
645 if (rc == VINF_SUCCESS)
646 {
647 /*
648 * Get bytes/words/dwords count to transfer.
649 */
650 RTGCUINTREG cTransfers = 1;
651 if (pCpu->prefix & PREFIX_REP)
652 {
653 cTransfers = pRegFrame->ecx;
654 if (!cTransfers)
655 return VINF_SUCCESS;
656 }
657
658 /* Convert source address ds:esi. */
659 RTGCPTR GCPtrSrc;
660 rc = SELMToFlatEx(pVM, pRegFrame->ds, (RTGCPTR)pRegFrame->esi,
661 SELMTOFLAT_FLAGS_HYPER | SELMTOFLAT_FLAGS_NO_PL,
662 &GCPtrSrc, NULL);
663 if (VBOX_FAILURE(rc))
664 {
665 Log(("OUTS source address conversion failed -> fallback, rc=%d\n", rc));
666 return VINF_IOM_HC_IOPORT_WRITE;
667 }
668
669 /* Access verification first; we currently can't recover properly from traps inside this instruction */
670 rc = PGMVerifyAccess(pVM, (RTGCUINTPTR)GCPtrSrc, cTransfers * cbSize,
671 ((pRegFrame->ss & X86_SEL_RPL) == 3) ? X86_PTE_US : 0);
672 if (rc != VINF_SUCCESS)
673 {
674 Log(("OUTS will generate a trap -> fallback, rc=%d\n", rc));
675 return VINF_IOM_HC_IOPORT_WRITE;
676 }
677
678 Log(("IOMGC: rep outs%d port %#x count %d\n", cbSize * 8, uPort, cTransfers));
679 MMGCRamRegisterTrapHandler(pVM);
680
681 /*
682 * If the device supports string transfers, ask it to do as
683 * much as it wants. The rest is done with single-word transfers.
684 */
685 const RTGCUINTREG cTransfersOrg = cTransfers;
686 rc = IOMIOPortWriteString(pVM, uPort, &GCPtrSrc, &cTransfers, cbSize);
687 AssertRC(rc); Assert(cTransfers <= cTransfersOrg);
688 pRegFrame->esi += (cTransfersOrg - cTransfers) * cbSize;
689
690 while (cTransfers && rc == VINF_SUCCESS)
691 {
692 uint32_t u32Value;
693 rc = MMGCRamReadNoTrapHandler(&u32Value, GCPtrSrc, cbSize);
694 if (rc != VINF_SUCCESS)
695 break;
696 rc = IOMIOPortWrite(pVM, uPort, u32Value, cbSize);
697 if (rc == VINF_IOM_HC_IOPORT_WRITE)
698 break;
699 GCPtrSrc = (RTGCPTR)((RTUINTPTR)GCPtrSrc + cbSize);
700 pRegFrame->esi += cbSize;
701 cTransfers--;
702 }
703
704 MMGCRamDeregisterTrapHandler(pVM);
705
706 /* Update ecx on exit. */
707 if (pCpu->prefix & PREFIX_REP)
708 pRegFrame->ecx = cTransfers;
709 }
710 return rc;
711}
712
713
714/**
715 * Attempts to service an IN/OUT instruction.
716 *
717 * The \#GP trap handler in GC will call this function if the opcode causing the
718 * trap is a in or out type instruction.
719 *
720 * @returns VBox status code.
721 *
722 * @param pVM The virtual machine (GC pointer ofcourse).
723 * @param pRegFrame Pointer to CPUMCTXCORE guest registers structure.
724 * @param pCpu Disassembler CPU state.
725 */
726IOMGCDECL(int) IOMGCIOPortHandler(PVM pVM, PCPUMCTXCORE pRegFrame, PDISCPUSTATE pCpu)
727{
728 switch (pCpu->pCurInstr->opcode)
729 {
730 case OP_IN:
731 return IOMInterpretIN(pVM, pRegFrame, pCpu);
732
733 case OP_OUT:
734 return IOMInterpretOUT(pVM, pRegFrame, pCpu);
735
736 case OP_INSB:
737 case OP_INSWD:
738 return IOMInterpretINS(pVM, pRegFrame, pCpu);
739
740 case OP_OUTSB:
741 case OP_OUTSWD:
742 return IOMInterpretOUTS(pVM, pRegFrame, pCpu);
743
744 /*
745 * The opcode wasn't know to us, freak out.
746 */
747 default:
748 AssertMsgFailed(("Unknown I/O port access opcode %d.\n", pCpu->pCurInstr->opcode));
749 return VERR_INTERNAL_ERROR;
750 }
751}
752
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