VirtualBox

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

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

SELM function changes for v86 mode code.
CPL check fixes for V86 mode code.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 24.2 KB
Line 
1/* $Id: IOMGC.cpp 1359 2007-03-09 10:40:44Z 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->eflags, 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 uint32_t cpl = (pRegFrame->eflags.Bits.u1VM) ? 3 : (pRegFrame->ss & X86_SEL_RPL);
567 rc = PGMVerifyAccess(pVM, (RTGCUINTPTR)GCPtrDst, cTransfers * cbSize,
568 X86_PTE_RW | ((cpl == 3) ? X86_PTE_US : 0));
569 if (rc != VINF_SUCCESS)
570 {
571 Log(("INS will generate a trap -> fallback, rc=%d\n", rc));
572 return VINF_IOM_HC_IOPORT_READ;
573 }
574
575 Log(("IOMGC: rep ins%d port %#x count %d\n", cbSize * 8, uPort, cTransfers));
576 MMGCRamRegisterTrapHandler(pVM);
577
578 /* If the device supports string transfers, ask it to do as
579 * much as it wants. The rest is done with single-word transfers. */
580 const RTGCUINTREG cTransfersOrg = cTransfers;
581 rc = IOMIOPortReadString(pVM, uPort, &GCPtrDst, &cTransfers, cbSize);
582 AssertRC(rc); Assert(cTransfers <= cTransfersOrg);
583 pRegFrame->edi += (cTransfersOrg - cTransfers) * cbSize;
584
585 while (cTransfers && rc == VINF_SUCCESS)
586 {
587 uint32_t u32Value;
588 rc = IOMIOPortRead(pVM, uPort, &u32Value, cbSize);
589 if (rc == VINF_IOM_HC_IOPORT_READ || VBOX_FAILURE(rc))
590 break;
591 int rc2 = MMGCRamWriteNoTrapHandler(GCPtrDst, &u32Value, cbSize);
592 Assert(rc2 == VINF_SUCCESS); NOREF(rc2);
593 GCPtrDst = (RTGCPTR)((RTGCUINTPTR)GCPtrDst + cbSize);
594 pRegFrame->edi += cbSize;
595 cTransfers--;
596 }
597 MMGCRamDeregisterTrapHandler(pVM);
598
599 /* Update ecx on exit. */
600 if (pCpu->prefix & PREFIX_REP)
601 pRegFrame->ecx = cTransfers;
602 }
603 return rc;
604}
605
606
607/**
608 * [REP*] OUTSB/OUTSW/OUTSD
609 * DS:ESI,DX[,ECX]
610 *
611 * @returns VBox status code.
612 *
613 * @param pVM The virtual machine (GC pointer ofcourse).
614 * @param pRegFrame Pointer to CPUMCTXCORE guest registers structure.
615 * @param pCpu Disassembler CPU state.
616 */
617IOMDECL(int) IOMInterpretOUTS(PVM pVM, PCPUMCTXCORE pRegFrame, PDISCPUSTATE pCpu)
618{
619#ifdef VBOX_WITH_STATISTICS
620 STAM_COUNTER_INC(&pVM->iom.s.StatGCInstOuts);
621#endif
622
623 /*
624 * We do not support segment prefixes, REPNE, 16-bit addressing or
625 * decrementing source pointer.
626 */
627 if ( pCpu->prefix & (PREFIX_SEG | PREFIX_REPNE)
628 || (pCpu->addrmode != CPUMODE_32BIT)
629 || pRegFrame->eflags.Bits.u1DF)
630 return VINF_IOM_HC_IOPORT_WRITE;
631
632 /*
633 * Get port number from the first parameter.
634 * And get the I/O register size from the opcode / prefix.
635 */
636 uint32_t uPort = 0;
637 unsigned cbSize = 0;
638 bool fRc = iomGCGetRegImmData(pCpu, &pCpu->param1, pRegFrame, &uPort, &cbSize);
639 AssertMsg(fRc, ("Failed to get reg/imm port number!\n")); NOREF(fRc);
640 if (pCpu->pCurInstr->opcode == OP_OUTSB)
641 cbSize = 1;
642 else
643 cbSize = pCpu->opmode == CPUMODE_32BIT ? 4 : 2;
644
645 int rc = IOMInterpretCheckPortIOAccess(pVM, pRegFrame, uPort, cbSize);
646 if (rc == VINF_SUCCESS)
647 {
648 /*
649 * Get bytes/words/dwords count to transfer.
650 */
651 RTGCUINTREG cTransfers = 1;
652 if (pCpu->prefix & PREFIX_REP)
653 {
654 cTransfers = pRegFrame->ecx;
655 if (!cTransfers)
656 return VINF_SUCCESS;
657 }
658
659 /* Convert source address ds:esi. */
660 RTGCPTR GCPtrSrc;
661 rc = SELMToFlatEx(pVM, pRegFrame->eflags, pRegFrame->ds, (RTGCPTR)pRegFrame->esi,
662 SELMTOFLAT_FLAGS_HYPER | SELMTOFLAT_FLAGS_NO_PL,
663 &GCPtrSrc, NULL);
664 if (VBOX_FAILURE(rc))
665 {
666 Log(("OUTS source address conversion failed -> fallback, rc=%d\n", rc));
667 return VINF_IOM_HC_IOPORT_WRITE;
668 }
669
670 /* Access verification first; we currently can't recover properly from traps inside this instruction */
671 uint32_t cpl = (pRegFrame->eflags.Bits.u1VM) ? 3 : (pRegFrame->ss & X86_SEL_RPL);
672 rc = PGMVerifyAccess(pVM, (RTGCUINTPTR)GCPtrSrc, cTransfers * cbSize,
673 (cpl == 3) ? X86_PTE_US : 0);
674 if (rc != VINF_SUCCESS)
675 {
676 Log(("OUTS will generate a trap -> fallback, rc=%d\n", rc));
677 return VINF_IOM_HC_IOPORT_WRITE;
678 }
679
680 Log(("IOMGC: rep outs%d port %#x count %d\n", cbSize * 8, uPort, cTransfers));
681 MMGCRamRegisterTrapHandler(pVM);
682
683 /*
684 * If the device supports string transfers, ask it to do as
685 * much as it wants. The rest is done with single-word transfers.
686 */
687 const RTGCUINTREG cTransfersOrg = cTransfers;
688 rc = IOMIOPortWriteString(pVM, uPort, &GCPtrSrc, &cTransfers, cbSize);
689 AssertRC(rc); Assert(cTransfers <= cTransfersOrg);
690 pRegFrame->esi += (cTransfersOrg - cTransfers) * cbSize;
691
692 while (cTransfers && rc == VINF_SUCCESS)
693 {
694 uint32_t u32Value;
695 rc = MMGCRamReadNoTrapHandler(&u32Value, GCPtrSrc, cbSize);
696 if (rc != VINF_SUCCESS)
697 break;
698 rc = IOMIOPortWrite(pVM, uPort, u32Value, cbSize);
699 if (rc == VINF_IOM_HC_IOPORT_WRITE)
700 break;
701 GCPtrSrc = (RTGCPTR)((RTUINTPTR)GCPtrSrc + cbSize);
702 pRegFrame->esi += cbSize;
703 cTransfers--;
704 }
705
706 MMGCRamDeregisterTrapHandler(pVM);
707
708 /* Update ecx on exit. */
709 if (pCpu->prefix & PREFIX_REP)
710 pRegFrame->ecx = cTransfers;
711 }
712 return rc;
713}
714
715
716/**
717 * Attempts to service an IN/OUT instruction.
718 *
719 * The \#GP trap handler in GC will call this function if the opcode causing the
720 * trap is a in or out type instruction.
721 *
722 * @returns VBox status code.
723 *
724 * @param pVM The virtual machine (GC pointer ofcourse).
725 * @param pRegFrame Pointer to CPUMCTXCORE guest registers structure.
726 * @param pCpu Disassembler CPU state.
727 */
728IOMGCDECL(int) IOMGCIOPortHandler(PVM pVM, PCPUMCTXCORE pRegFrame, PDISCPUSTATE pCpu)
729{
730 switch (pCpu->pCurInstr->opcode)
731 {
732 case OP_IN:
733 return IOMInterpretIN(pVM, pRegFrame, pCpu);
734
735 case OP_OUT:
736 return IOMInterpretOUT(pVM, pRegFrame, pCpu);
737
738 case OP_INSB:
739 case OP_INSWD:
740 return IOMInterpretINS(pVM, pRegFrame, pCpu);
741
742 case OP_OUTSB:
743 case OP_OUTSWD:
744 return IOMInterpretOUTS(pVM, pRegFrame, pCpu);
745
746 /*
747 * The opcode wasn't know to us, freak out.
748 */
749 default:
750 AssertMsgFailed(("Unknown I/O port access opcode %d.\n", pCpu->pCurInstr->opcode));
751 return VERR_INTERNAL_ERROR;
752 }
753}
754
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