VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/IOMAll.cpp@ 4284

Last change on this file since 4284 was 4071, checked in by vboxsync, 17 years ago

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

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 50.1 KB
Line 
1/* $Id: IOMAll.cpp 4071 2007-08-07 17:07:59Z vboxsync $ */
2/** @file
3 * IOM - Input / Output Monitor - Any Context.
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_IOM
22#include <VBox/iom.h>
23#include <VBox/mm.h>
24#include <VBox/param.h>
25#include "IOMInternal.h"
26#include <VBox/vm.h>
27#include <VBox/selm.h>
28#include <VBox/trpm.h>
29#include <VBox/pgm.h>
30#include <VBox/cpum.h>
31#include <VBox/err.h>
32#include <VBox/log.h>
33#include <iprt/assert.h>
34
35
36/*******************************************************************************
37* Global Variables *
38*******************************************************************************/
39
40/**
41 * Array for fast recode of the operand size (1/2/4/8 bytes) to bit shift value.
42 */
43static const unsigned g_aSize2Shift[] =
44{
45 ~0, /* 0 - invalid */
46 0, /* *1 == 2^0 */
47 1, /* *2 == 2^1 */
48 ~0, /* 3 - invalid */
49 2, /* *4 == 2^2 */
50 ~0, /* 5 - invalid */
51 ~0, /* 6 - invalid */
52 ~0, /* 7 - invalid */
53 3 /* *8 == 2^3 */
54};
55
56/**
57 * Macro for fast recode of the operand size (1/2/4/8 bytes) to bit shift value.
58 */
59#define SIZE2SHIFT(cb) (g_aSize2Shift[cb])
60
61/**
62 * Calculates the size of register parameter.
63 *
64 * @returns 1, 2, 4 on success.
65 * @returns 0 if non-register parameter.
66 * @param pCpu Pointer to current disassembler context.
67 * @param pParam Pointer to parameter of instruction to proccess.
68 */
69static unsigned iomGCGetRegSize(PDISCPUSTATE pCpu, PCOP_PARAMETER pParam)
70{
71 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))
72 return 0;
73
74 if (pParam->flags & USE_REG_GEN32)
75 return 4;
76
77 if (pParam->flags & USE_REG_GEN16)
78 return 2;
79
80 if (pParam->flags & USE_REG_GEN8)
81 return 1;
82
83 if (pParam->flags & USE_REG_SEG)
84 return 2;
85 return 0;
86}
87
88/**
89 * Returns the contents of register or immediate data of instruction's parameter.
90 *
91 * @returns true on success.
92 *
93 * @param pCpu Pointer to current disassembler context.
94 * @param pParam Pointer to parameter of instruction to proccess.
95 * @param pRegFrame Pointer to CPUMCTXCORE guest structure.
96 * @param pu32Data Where to store retrieved data.
97 * @param pcbSize Where to store the size of data (1, 2, 4).
98 */
99static bool iomGCGetRegImmData(PDISCPUSTATE pCpu, PCOP_PARAMETER pParam, PCPUMCTXCORE pRegFrame, uint32_t *pu32Data, unsigned *pcbSize)
100{
101 if (pParam->flags & (USE_BASE | USE_INDEX | USE_SCALE | USE_DISPLACEMENT8 | USE_DISPLACEMENT16 | USE_DISPLACEMENT32))
102 {
103 *pcbSize = 0;
104 *pu32Data = 0;
105 return false;
106 }
107
108 if (pParam->flags & USE_REG_GEN32)
109 {
110 *pcbSize = 4;
111 DISFetchReg32(pRegFrame, pParam->base.reg_gen32, pu32Data);
112 return true;
113 }
114
115 if (pParam->flags & USE_REG_GEN16)
116 {
117 *pcbSize = 2;
118 DISFetchReg16(pRegFrame, pParam->base.reg_gen16, (uint16_t *)pu32Data);
119 return true;
120 }
121
122 if (pParam->flags & USE_REG_GEN8)
123 {
124 *pcbSize = 1;
125 DISFetchReg8(pRegFrame, pParam->base.reg_gen8, (uint8_t *)pu32Data);
126 return true;
127 }
128
129 if (pParam->flags & (USE_IMMEDIATE32|USE_IMMEDIATE32_SX8))
130 {
131 *pcbSize = 4;
132 *pu32Data = (uint32_t)pParam->parval;
133 return true;
134 }
135
136 if (pParam->flags & (USE_IMMEDIATE16|USE_IMMEDIATE16_SX8))
137 {
138 *pcbSize = 2;
139 *pu32Data = (uint16_t)pParam->parval;
140 return true;
141 }
142
143 if (pParam->flags & USE_IMMEDIATE8)
144 {
145 *pcbSize = 1;
146 *pu32Data = (uint8_t)pParam->parval;
147 return true;
148 }
149
150 if (pParam->flags & USE_REG_SEG)
151 {
152 *pcbSize = 2;
153 DISFetchRegSeg(pRegFrame, pParam->base.reg_seg, (RTSEL *)pu32Data);
154 return true;
155 } /* Else - error. */
156
157 *pcbSize = 0;
158 *pu32Data = 0;
159 return false;
160}
161
162
163/**
164 * Saves data to 8/16/32 general purpose or segment register defined by
165 * instruction's parameter.
166 *
167 * @returns true on success.
168 * @param pCpu Pointer to current disassembler context.
169 * @param pParam Pointer to parameter of instruction to proccess.
170 * @param pRegFrame Pointer to CPUMCTXCORE guest structure.
171 * @param u32Data 8/16/32 bit data to store.
172 */
173static bool iomGCSaveDataToReg(PDISCPUSTATE pCpu, PCOP_PARAMETER pParam, PCPUMCTXCORE pRegFrame, unsigned u32Data)
174{
175 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))
176 {
177 return false;
178 }
179
180 if (pParam->flags & USE_REG_GEN32)
181 {
182 DISWriteReg32(pRegFrame, pParam->base.reg_gen32, u32Data);
183 return true;
184 }
185
186 if (pParam->flags & USE_REG_GEN16)
187 {
188 DISWriteReg16(pRegFrame, pParam->base.reg_gen16, (uint16_t)u32Data);
189 return true;
190 }
191
192 if (pParam->flags & USE_REG_GEN8)
193 {
194 DISWriteReg8(pRegFrame, pParam->base.reg_gen8, (uint8_t)u32Data);
195 return true;
196 }
197
198 if (pParam->flags & USE_REG_SEG)
199 {
200 DISWriteRegSeg(pRegFrame, pParam->base.reg_seg, (RTSEL)u32Data);
201 return true;
202 }
203
204 /* Else - error. */
205 return false;
206}
207
208/*
209 * Internal - statistics only.
210 */
211inline void iomGCMMIOStatLength(PVM pVM, unsigned cb)
212{
213#ifdef VBOX_WITH_STATISTICS
214 switch (cb)
215 {
216 case 1:
217 STAM_COUNTER_INC(&pVM->iom.s.StatGCMMIO1Byte);
218 break;
219 case 2:
220 STAM_COUNTER_INC(&pVM->iom.s.StatGCMMIO2Bytes);
221 break;
222 case 4:
223 STAM_COUNTER_INC(&pVM->iom.s.StatGCMMIO4Bytes);
224 break;
225 default:
226 /* No way. */
227 AssertMsgFailed(("Invalid data length %d\n", cb));
228 break;
229 }
230#else
231 NOREF(pVM); NOREF(cb);
232#endif
233}
234
235/**
236 * Registers a Port IO GC handler.
237 *
238 * This API is called by PDM on behalf of a device. Devices must first register ring-3 ranges
239 * using IOMIOPortRegisterR3() before calling this function.
240 *
241 *
242 * @returns VBox status code.
243 *
244 * @param pVM VM handle.
245 * @param pDevIns PDM device instance owning the port range.
246 * @param PortStart First port number in the range.
247 * @param cPorts Number of ports to register.
248 * @param pvUser User argument for the callbacks.
249 * @param pfnOutCallback Pointer to function which is gonna handle OUT operations in GC.
250 * @param pfnInCallback Pointer to function which is gonna handle IN operations in GC.
251 * @param pfnOutStrCallback Pointer to function which is gonna handle string OUT operations in GC.
252 * @param pfnInStrCallback Pointer to function which is gonna handle string IN operations in GC.
253 * @param pszDesc Pointer to description string. This must not be freed.
254 */
255IOMDECL(int) IOMIOPortRegisterGC(PVM pVM, PPDMDEVINS pDevIns, RTIOPORT PortStart, RTUINT cPorts, RTGCPTR pvUser,
256 GCPTRTYPE(PFNIOMIOPORTOUT) pfnOutCallback, GCPTRTYPE(PFNIOMIOPORTIN) pfnInCallback,
257 GCPTRTYPE(PFNIOMIOPORTOUTSTRING) pfnOutStrCallback, GCPTRTYPE(PFNIOMIOPORTINSTRING) pfnInStrCallback, const char *pszDesc)
258{
259 LogFlow(("IOMIOPortRegisterGC: pDevIns=%p PortStart=%#x cPorts=%#x pvUser=%VGv pfnOutCallback=%VGv pfnInCallback=%VGv pfnOutStrCallback=%VGv pfnInStrCallback=%VGv pszDesc=%s\n",
260 pDevIns, PortStart, cPorts, pvUser, pfnOutCallback, pfnInCallback, pfnOutStrCallback, pfnInStrCallback, pszDesc));
261
262 /*
263 * Validate input.
264 */
265 if ( (RTUINT)PortStart + cPorts <= (RTUINT)PortStart
266 || (RTUINT)PortStart + cPorts > 0x10000)
267 {
268 AssertMsgFailed(("Invalid port range %#x-%#x! (%s)\n", PortStart, (RTUINT)PortStart + (cPorts - 1), pszDesc));
269 return VERR_IOM_INVALID_IOPORT_RANGE;
270 }
271 RTIOPORT PortLast = PortStart + (cPorts - 1);
272 if (!pfnOutCallback && !pfnInCallback)
273 {
274 AssertMsgFailed(("Invalid port range %#x-%#x! No callbacks! (%s)\n", PortStart, PortLast, pszDesc));
275 return VERR_INVALID_PARAMETER;
276 }
277
278 /*
279 * Validate that there are ring-3 ranges for the ports.
280 */
281 RTIOPORT Port = PortStart;
282 while (Port <= PortLast && Port >= PortStart)
283 {
284 PIOMIOPORTRANGER3 pRange = (PIOMIOPORTRANGER3)RTAvlroIOPortRangeGet(&pVM->iom.s.CTXSUFF(pTrees)->IOPortTreeR3, Port);
285 if (!pRange)
286 {
287 AssertMsgFailed(("No R3! Port=#x %#x-%#x! (%s)\n", Port, PortStart, (unsigned)PortStart + cPorts - 1, pszDesc));
288 return VERR_IOM_NO_HC_IOPORT_RANGE;
289 }
290#ifndef IOM_NO_PDMINS_CHECKS
291 #ifndef IN_GC
292 if (pRange->pDevIns != pDevIns)
293 #else
294 if (pRange->pDevIns != MMHyperGC2HC(pVM, pDevIns))
295 #endif
296 {
297 AssertMsgFailed(("Not owner! Port=%#x %#x-%#x! (%s)\n", Port, PortStart, (unsigned)PortStart + cPorts - 1, pszDesc));
298 return VERR_IOM_NOT_IOPORT_RANGE_OWNER;
299 }
300#endif
301 Port = pRange->Core.KeyLast + 1;
302 }
303
304 /* Flush the IO port lookup cache */
305 IOMFlushCache(pVM);
306
307 /*
308 * Allocate new range record and initialize it.
309 */
310 PIOMIOPORTRANGEGC pRange;
311 int rc = MMHyperAlloc(pVM, sizeof(*pRange), 0, MM_TAG_IOM, (void **)&pRange);
312 if (VBOX_SUCCESS(rc))
313 {
314 pRange->Core.Key = PortStart;
315 pRange->Core.KeyLast = PortLast;
316 pRange->Port = PortStart;
317 pRange->cPorts = cPorts;
318 pRange->pvUser = pvUser;
319 pRange->pfnOutCallback = pfnOutCallback;
320 pRange->pfnInCallback = pfnInCallback;
321 pRange->pfnOutStrCallback = pfnOutStrCallback;
322 pRange->pfnInStrCallback = pfnInStrCallback;
323 #ifdef IN_GC
324 pRange->pDevIns = pDevIns;
325 pRange->pszDesc = MMHyperGC2HC(pVM, (void *)pszDesc);
326 #else
327 pRange->pDevIns = MMHyperHC2GC(pVM, pDevIns);
328 pRange->pszDesc = pszDesc;
329 #endif
330
331 /*
332 * Insert it.
333 */
334 if (RTAvlroIOPortInsert(&pVM->iom.s.CTXSUFF(pTrees)->IOPortTreeGC, &pRange->Core))
335 return VINF_SUCCESS;
336
337 /* conflict. */
338 AssertMsgFailed(("Port range %#x-%#x (%s) conflicts with existing range(s)!\n", PortStart, (unsigned)PortStart + cPorts - 1, pszDesc));
339 MMHyperFree(pVM, pRange);
340 rc = VERR_IOM_IOPORT_RANGE_CONFLICT;
341 }
342
343 return rc;
344}
345
346
347/**
348 * Registers a Memory Mapped I/O GC handler range.
349 *
350 * This API is called by PDM on behalf of a device. Devices must first register ring-3 ranges
351 * using IOMMMIORegisterR3() before calling this function.
352 *
353 *
354 * @returns VBox status code.
355 *
356 * @param pVM VM handle.
357 * @param pDevIns PDM device instance owning the MMIO range.
358 * @param GCPhysStart First physical address in the range.
359 * @param cbRange The size of the range (in bytes).
360 * @param pvUser User argument for the callbacks.
361 * @param pfnWriteCallback Pointer to function which is gonna handle Write operations.
362 * @param pfnReadCallback Pointer to function which is gonna handle Read operations.
363 * @param pfnFillCallback Pointer to function which is gonna handle Fill/memset operations.
364 * @param pszDesc Pointer to description string. This must not be freed.
365 */
366IOMDECL(int) IOMMMIORegisterGC(PVM pVM, PPDMDEVINS pDevIns, RTGCPHYS GCPhysStart, RTUINT cbRange, RTGCPTR pvUser,
367 GCPTRTYPE(PFNIOMMMIOWRITE) pfnWriteCallback, GCPTRTYPE(PFNIOMMMIOREAD) pfnReadCallback,
368 GCPTRTYPE(PFNIOMMMIOFILL) pfnFillCallback, const char *pszDesc)
369{
370 LogFlow(("IOMMMIORegisterGC: pDevIns=%p GCPhysStart=%#x cbRange=%#x pvUser=%VGv pfnWriteCallback=%#x pfnReadCallback=%#x pfnFillCallback=%#x pszDesc=%s\n",
371 pDevIns, GCPhysStart, cbRange, pvUser, pfnWriteCallback, pfnReadCallback, pfnFillCallback, pszDesc));
372
373 /*
374 * Validate input.
375 */
376 if (!pfnWriteCallback && !pfnReadCallback)
377 {
378 AssertMsgFailed(("No callbacks! %#x LB%#x %s\n", GCPhysStart, cbRange, pszDesc));
379 return VERR_INVALID_PARAMETER;
380 }
381 RTGCPHYS GCPhysLast = GCPhysStart + (cbRange - 1);
382 if (GCPhysLast < GCPhysStart)
383 {
384 AssertMsgFailed(("Wrapped! %#x LB%#x %s\n", GCPhysStart, cbRange, pszDesc));
385 return VERR_IOM_INVALID_MMIO_RANGE;
386 }
387
388 /*
389 * Check that a ring-3 MMIO range exists.
390 */
391 RTGCPHYS GCPhys = GCPhysStart;
392 while (GCPhys <= GCPhysLast && GCPhys >= GCPhysStart)
393 {
394 PIOMMMIORANGER3 pRange = (PIOMMMIORANGER3)RTAvlroGCPhysRangeGet(&pVM->iom.s.CTXSUFF(pTrees)->MMIOTreeR3, GCPhys);
395 if (!pRange)
396 {
397 AssertMsgFailed(("No R3 range! GCPhys=%#x %#x LB%#x %s\n", GCPhys, GCPhysStart, cbRange, pszDesc));
398 return VERR_IOM_NO_HC_MMIO_RANGE;
399 }
400#ifndef IOM_NO_PDMINS_CHECKS
401 #ifndef IN_GC
402 if (pRange->pDevIns != pDevIns)
403 #else
404 if (pRange->pDevIns != MMHyperGC2HC(pVM, pDevIns))
405 #endif
406 {
407 AssertMsgFailed(("Not owner! GCPhys=%#x %#x LB%#x %s / %#x-%#x %s\n", GCPhys, GCPhysStart, cbRange, pszDesc,
408 pRange->Core.Key, pRange->Core.KeyLast, MMHyper2HC(pVM, (uintptr_t)pRange->pszDesc)));
409 return VERR_IOM_NOT_MMIO_RANGE_OWNER;
410 }
411#endif /* !IOM_NO_PDMINS_CHECKS */
412 /* next */
413 Assert(GCPhys <= pRange->Core.KeyLast);
414 GCPhys = pRange->Core.KeyLast + 1;
415 }
416
417
418 /*
419 * Allocate new range record and initialize it.
420 */
421 PIOMMMIORANGEGC pRange;
422 int rc = MMHyperAlloc(pVM, sizeof(*pRange), 0, MM_TAG_IOM, (void **)&pRange);
423 if (VBOX_SUCCESS(rc))
424 {
425 pRange->Core.Key = GCPhysStart;
426 pRange->Core.KeyLast = GCPhysStart + (cbRange - 1);
427 pRange->GCPhys = GCPhysStart;
428 pRange->cbSize = cbRange;
429 pRange->pvUser = pvUser;
430 pRange->pfnReadCallback = pfnReadCallback;
431 pRange->pfnWriteCallback= pfnWriteCallback;
432 pRange->pfnFillCallback = pfnFillCallback;
433#ifdef IN_GC
434 pRange->pDevIns = pDevIns;
435 pRange->pszDesc = MMHyperGC2HC(pVM, (void *)pszDesc);
436#else
437 pRange->pDevIns = MMHyperHC2GC(pVM, pDevIns);
438 pRange->pszDesc = pszDesc;
439#endif
440
441 /*
442 * Try insert it.
443 */
444 if (RTAvlroGCPhysInsert(&pVM->iom.s.CTXSUFF(pTrees)->MMIOTreeGC, &pRange->Core))
445 return VINF_SUCCESS;
446
447 AssertMsgFailed(("Conflict! %#x LB%#x %s\n", GCPhysStart, cbRange, pszDesc));
448 MMHyperFree(pVM, pRange);
449 rc = VERR_IOM_MMIO_RANGE_CONFLICT;
450 }
451
452 return rc;
453}
454
455
456/**
457 * Registers a Port IO R0 handler.
458 *
459 * This API is called by PDM on behalf of a device. Devices must first register ring-3 ranges
460 * using IOMR3IOPortRegisterR3() before calling this function.
461 *
462 *
463 * @returns VBox status code.
464 *
465 * @param pVM VM handle.
466 * @param pDevIns PDM device instance owning the port range.
467 * @param PortStart First port number in the range.
468 * @param cPorts Number of ports to register.
469 * @param pvUser User argument for the callbacks.
470 * @param pfnOutCallback Pointer to function which is gonna handle OUT operations in GC.
471 * @param pfnInCallback Pointer to function which is gonna handle IN operations in GC.
472 * @param pfnOutStrCallback Pointer to function which is gonna handle OUT operations in GC.
473 * @param pfnInStrCallback Pointer to function which is gonna handle IN operations in GC.
474 * @param pszDesc Pointer to description string. This must not be freed.
475 */
476IOMDECL(int) IOMIOPortRegisterR0(PVM pVM, PPDMDEVINS pDevIns, RTIOPORT PortStart, RTUINT cPorts, RTR0PTR pvUser,
477 R0PTRTYPE(PFNIOMIOPORTOUT) pfnOutCallback, R0PTRTYPE(PFNIOMIOPORTIN) pfnInCallback,
478 R0PTRTYPE(PFNIOMIOPORTOUTSTRING) pfnOutStrCallback, R0PTRTYPE(PFNIOMIOPORTINSTRING) pfnInStrCallback,
479 const char *pszDesc)
480{
481 LogFlow(("IOMIOPortRegisterR0: pDevIns=%p PortStart=%#x cPorts=%#x pvUser=%VHv pfnOutCallback=%VGv pfnInCallback=%VGv pfnOutStrCallback=%VGv pfnInStrCallback=%VGv pszDesc=%s\n",
482 pDevIns, PortStart, cPorts, pvUser, pfnOutCallback, pfnInCallback, pfnOutStrCallback, pfnInStrCallback, pszDesc));
483
484 /*
485 * Validate input.
486 */
487 if ( (RTUINT)PortStart + cPorts <= (RTUINT)PortStart
488 || (RTUINT)PortStart + cPorts > 0x10000)
489 {
490 AssertMsgFailed(("Invalid port range %#x-%#x! (%s)\n", PortStart, (RTUINT)PortStart + (cPorts - 1), pszDesc));
491 return VERR_IOM_INVALID_IOPORT_RANGE;
492 }
493 RTIOPORT PortLast = PortStart + (cPorts - 1);
494 if (!pfnOutCallback && !pfnInCallback)
495 {
496 AssertMsgFailed(("Invalid port range %#x-%#x! No callbacks! (%s)\n", PortStart, PortLast, pszDesc));
497 return VERR_INVALID_PARAMETER;
498 }
499
500 /*
501 * Validate that there are ring-3 ranges for the ports.
502 */
503 RTIOPORT Port = PortStart;
504 while (Port <= PortLast && Port >= PortStart)
505 {
506 PIOMIOPORTRANGER3 pRange = (PIOMIOPORTRANGER3)RTAvlroIOPortRangeGet(&pVM->iom.s.CTXSUFF(pTrees)->IOPortTreeR3, Port);
507 if (!pRange)
508 {
509 AssertMsgFailed(("No R3! Port=#x %#x-%#x! (%s)\n", Port, PortStart, (unsigned)PortStart + cPorts - 1, pszDesc));
510 return VERR_IOM_NO_HC_IOPORT_RANGE;
511 }
512#ifndef IOM_NO_PDMINS_CHECKS
513# ifndef IN_GC
514 if (pRange->pDevIns != pDevIns)
515# else
516 if (pRange->pDevIns != MMHyperGC2HC(pVM, pDevIns))
517# endif
518 {
519 AssertMsgFailed(("Not owner! Port=%#x %#x-%#x! (%s)\n", Port, PortStart, (unsigned)PortStart + cPorts - 1, pszDesc));
520 return VERR_IOM_NOT_IOPORT_RANGE_OWNER;
521 }
522#endif
523 Port = pRange->Core.KeyLast + 1;
524 }
525
526 /* Flush the IO port lookup cache */
527 IOMFlushCache(pVM);
528
529 /*
530 * Allocate new range record and initialize it.
531 */
532 PIOMIOPORTRANGER0 pRange;
533 int rc = MMHyperAlloc(pVM, sizeof(*pRange), 0, MM_TAG_IOM, (void **)&pRange);
534 if (VBOX_SUCCESS(rc))
535 {
536 pRange->Core.Key = PortStart;
537 pRange->Core.KeyLast = PortLast;
538 pRange->Port = PortStart;
539 pRange->cPorts = cPorts;
540 pRange->pvUser = pvUser;
541 pRange->pfnOutCallback = pfnOutCallback;
542 pRange->pfnInCallback = pfnInCallback;
543 pRange->pfnOutStrCallback = pfnOutStrCallback;
544 pRange->pfnInStrCallback = pfnInStrCallback;
545#ifdef IN_GC
546 pRange->pDevIns = MMHyperGCToR0(pVM, pDevIns);
547 pRange->pszDesc = MMHyperGCToR3(pVM, (void *)pszDesc);
548#elif defined(IN_RING3)
549 pRange->pDevIns = MMHyperR3ToR0(pVM, pDevIns);
550 pRange->pszDesc = pszDesc;
551#else
552 pRange->pDevIns = pDevIns;
553 pRange->pszDesc = MMHyperR0ToR3(pVM, (RTR0PTR)pszDesc);
554#endif
555
556 /*
557 * Insert it.
558 */
559 if (RTAvlroIOPortInsert(&pVM->iom.s.CTXSUFF(pTrees)->IOPortTreeR0, &pRange->Core))
560 return VINF_SUCCESS;
561
562 /* conflict. */
563 AssertMsgFailed(("Port range %#x-%#x (%s) conflicts with existing range(s)!\n", PortStart, (unsigned)PortStart + cPorts - 1, pszDesc));
564 MMHyperFree(pVM, pRange);
565 rc = VERR_IOM_IOPORT_RANGE_CONFLICT;
566 }
567
568 return rc;
569}
570
571
572/**
573 * Registers a Memory Mapped I/O R0 handler range.
574 *
575 * This API is called by PDM on behalf of a device. Devices must first register ring-3 ranges
576 * using IOMMR3MIORegisterHC() before calling this function.
577 *
578 *
579 * @returns VBox status code.
580 *
581 * @param pVM VM handle.
582 * @param pDevIns PDM device instance owning the MMIO range.
583 * @param GCPhysStart First physical address in the range.
584 * @param cbRange The size of the range (in bytes).
585 * @param pvUser User argument for the callbacks.
586 * @param pfnWriteCallback Pointer to function which is gonna handle Write operations.
587 * @param pfnReadCallback Pointer to function which is gonna handle Read operations.
588 * @param pfnFillCallback Pointer to function which is gonna handle Fill/memset operations.
589 * @param pszDesc Pointer to description string. This must not be freed.
590 */
591IOMDECL(int) IOMMMIORegisterR0(PVM pVM, PPDMDEVINS pDevIns, RTGCPHYS GCPhysStart, RTUINT cbRange, RTR0PTR pvUser,
592 R0PTRTYPE(PFNIOMMMIOWRITE) pfnWriteCallback, R0PTRTYPE(PFNIOMMMIOREAD) pfnReadCallback,
593 R0PTRTYPE(PFNIOMMMIOFILL) pfnFillCallback, const char *pszDesc)
594{
595 LogFlow(("IOMMMIORegisterR0: pDevIns=%p GCPhysStart=%#x cbRange=%#x pvUser=%VHv pfnWriteCallback=%#x pfnReadCallback=%#x pfnFillCallback=%#x pszDesc=%s\n",
596 pDevIns, GCPhysStart, cbRange, pvUser, pfnWriteCallback, pfnReadCallback, pfnFillCallback, pszDesc));
597
598 /*
599 * Validate input.
600 */
601 if (!pfnWriteCallback && !pfnReadCallback)
602 {
603 AssertMsgFailed(("No callbacks! %#x LB%#x %s\n", GCPhysStart, cbRange, pszDesc));
604 return VERR_INVALID_PARAMETER;
605 }
606 RTGCPHYS GCPhysLast = GCPhysStart + (cbRange - 1);
607 if (GCPhysLast < GCPhysStart)
608 {
609 AssertMsgFailed(("Wrapped! %#x LB%#x %s\n", GCPhysStart, cbRange, pszDesc));
610 return VERR_IOM_INVALID_MMIO_RANGE;
611 }
612
613 /*
614 * Check that a ring-3 MMIO range exists.
615 */
616 RTGCPHYS GCPhys = GCPhysStart;
617 while (GCPhys <= GCPhysLast && GCPhys >= GCPhysStart)
618 {
619 PIOMMMIORANGER3 pRange = (PIOMMMIORANGER3)RTAvlroGCPhysRangeGet(&pVM->iom.s.CTXSUFF(pTrees)->MMIOTreeR3, GCPhys);
620 if (!pRange)
621 {
622 AssertMsgFailed(("No R3 range! GCPhys=%#x %#x LB%#x %s\n", GCPhys, GCPhysStart, cbRange, pszDesc));
623 return VERR_IOM_NO_HC_MMIO_RANGE;
624 }
625#ifndef IOM_NO_PDMINS_CHECKS
626# ifndef IN_GC
627 if (pRange->pDevIns != pDevIns)
628# else
629 if (pRange->pDevIns != MMHyperGC2HC(pVM, pDevIns))
630# endif
631 {
632 AssertMsgFailed(("Not owner! GCPhys=%#x %#x LB%#x %s / %#x-%#x %s\n", GCPhys, GCPhysStart, cbRange, pszDesc,
633 pRange->Core.Key, pRange->Core.KeyLast, MMHyper2HC(pVM, (uintptr_t)pRange->pszDesc)));
634 return VERR_IOM_NOT_MMIO_RANGE_OWNER;
635 }
636#endif /* !IOM_NO_PDMINS_CHECKS */
637 /* next */
638 Assert(GCPhys <= pRange->Core.KeyLast);
639 GCPhys = pRange->Core.KeyLast + 1;
640 }
641
642
643 /*
644 * Allocate new range record and initialize it.
645 */
646 PIOMMMIORANGER0 pRange;
647 int rc = MMHyperAlloc(pVM, sizeof(*pRange), 0, MM_TAG_IOM, (void **)&pRange);
648 if (VBOX_SUCCESS(rc))
649 {
650 pRange->Core.Key = GCPhysStart;
651 pRange->Core.KeyLast = GCPhysStart + (cbRange - 1);
652 pRange->GCPhys = GCPhysStart;
653 pRange->cbSize = cbRange;
654 pRange->pvUser = pvUser;
655 pRange->pfnReadCallback = pfnReadCallback;
656 pRange->pfnWriteCallback= pfnWriteCallback;
657 pRange->pfnFillCallback = pfnFillCallback;
658#ifdef IN_GC
659 pRange->pDevIns = MMHyperGCToR0(pVM, pDevIns);
660 pRange->pszDesc = MMHyperGCToR3(pVM, (void *)pszDesc);
661#elif defined(IN_RING3)
662 pRange->pDevIns = MMHyperR3ToR0(pVM, pDevIns);
663 pRange->pszDesc = pszDesc;
664#else
665 pRange->pDevIns = pDevIns;
666 pRange->pszDesc = MMHyperR0ToR3(pVM, (RTR0PTR)pszDesc);
667#endif
668
669 /*
670 * Try insert it.
671 */
672 if (RTAvlroGCPhysInsert(&pVM->iom.s.CTXSUFF(pTrees)->MMIOTreeR0, &pRange->Core))
673 return VINF_SUCCESS;
674
675 AssertMsgFailed(("Conflict! %#x LB%#x %s\n", GCPhysStart, cbRange, pszDesc));
676 MMHyperFree(pVM, pRange);
677 rc = VERR_IOM_MMIO_RANGE_CONFLICT;
678 }
679
680 return rc;
681}
682
683
684/**
685 * Flushes the IOM port & statistics lookup cache
686 *
687 * @param pVM The VM.
688 */
689IOMDECL(void) IOMFlushCache(PVM pVM)
690{
691 /*
692 * Caching of port and statistics (saves some time in rep outs/ins instruction emulation)
693 */
694 pVM->iom.s.pRangeLastReadGC = 0;
695 pVM->iom.s.pRangeLastWriteGC = 0;
696 pVM->iom.s.pStatsLastReadGC = 0;
697 pVM->iom.s.pStatsLastWriteGC = 0;
698
699 pVM->iom.s.pRangeLastReadR3 = 0;
700 pVM->iom.s.pRangeLastWriteR3 = 0;
701 pVM->iom.s.pStatsLastReadR3 = 0;
702 pVM->iom.s.pStatsLastWriteR3 = 0;
703
704 pVM->iom.s.pRangeLastReadR0 = 0;
705 pVM->iom.s.pRangeLastWriteR0 = 0;
706 pVM->iom.s.pStatsLastReadR0 = 0;
707 pVM->iom.s.pStatsLastWriteR0 = 0;
708}
709
710
711//#undef LOG_GROUP
712//#define LOG_GROUP LOG_GROUP_IOM_IOPORT
713
714/**
715 * Reads an I/O port register.
716 *
717 * @returns Strict VBox status code. Informational status codes other than the one documented
718 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
719 * @retval VINF_SUCCESS Success.
720 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
721 * status code must be passed on to EM.
722 * @retval VINF_IOM_HC_IOPORT_READ Defer the read to ring-3. (R0/GC only)
723 *
724 * @param pVM VM handle.
725 * @param Port The port to read.
726 * @param pu32Value Where to store the value read.
727 * @param cbValue The size of the register to read in bytes. 1, 2 or 4 bytes.
728 */
729IOMDECL(int) IOMIOPortRead(PVM pVM, RTIOPORT Port, uint32_t *pu32Value, size_t cbValue)
730{
731#ifdef VBOX_WITH_STATISTICS
732 /*
733 * Get the statistics record.
734 */
735 PIOMIOPORTSTATS pStats = CTXALLSUFF(pVM->iom.s.pStatsLastRead);
736 if (!pStats || pStats->Core.Key != Port)
737 {
738 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTXSUFF(pTrees)->IOPortStatTree, Port);
739 if (pStats)
740 CTXALLSUFF(pVM->iom.s.pStatsLastRead) = pStats;
741 }
742#endif
743
744 /*
745 * Get handler for current context.
746 */
747 CTXALLSUFF(PIOMIOPORTRANGE) pRange = CTXALLSUFF(pVM->iom.s.pRangeLastRead);
748 if ( !pRange
749 || (unsigned)Port - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
750 {
751 pRange = iomIOPortGetRange(&pVM->iom.s, Port);
752 if (pRange)
753 CTXALLSUFF(pVM->iom.s.pRangeLastRead) = pRange;
754 }
755#ifdef IN_GC
756 Assert(!pRange || MMHyperIsInsideArea(pVM, pRange)); /** @todo r=bird: there is a macro for this which skips the #if'ing. */
757#endif
758
759 if (pRange)
760 {
761 /*
762 * Found a range.
763 */
764#ifndef IN_RING3
765 if (!pRange->pfnInCallback)
766 {
767# ifdef VBOX_WITH_STATISTICS
768 if (pStats)
769 STAM_COUNTER_INC(&pStats->CTXALLMID(In,ToR3));
770# endif
771 return VINF_IOM_HC_IOPORT_READ;
772 }
773#endif
774 /* call the device. */
775#ifdef VBOX_WITH_STATISTICS
776 if (pStats)
777 STAM_PROFILE_ADV_START(&pStats->CTXALLSUFF(ProfIn), a);
778#endif
779 int rc = pRange->pfnInCallback(pRange->pDevIns, pRange->pvUser, Port, pu32Value, cbValue);
780#ifdef VBOX_WITH_STATISTICS
781 if (pStats)
782 STAM_PROFILE_ADV_STOP(&pStats->CTXALLSUFF(ProfIn), a);
783 if (rc == VINF_SUCCESS && pStats)
784 STAM_COUNTER_INC(&pStats->CTXALLSUFF(In));
785# ifndef IN_RING3
786 else if (rc == VINF_IOM_HC_IOPORT_READ && pStats)
787 STAM_COUNTER_INC(&pStats->CTXALLMID(In,ToR3));
788# endif
789#endif
790 if (rc == VERR_IOM_IOPORT_UNUSED)
791 {
792 /* make return value */
793 rc = VINF_SUCCESS;
794 switch (cbValue)
795 {
796 case 1: *(uint8_t *)pu32Value = 0xff; break;
797 case 2: *(uint16_t *)pu32Value = 0xffff; break;
798 case 4: *(uint32_t *)pu32Value = 0xffffffff; break;
799 default:
800 AssertMsgFailed(("Invalid I/O port size %d. Port=%d\n", cbValue, Port));
801 return VERR_IOM_INVALID_IOPORT_SIZE;
802 }
803 }
804 Log3(("IOMIOPortRead: Port=%RTiop *pu32=%08RX32 cb=%d rc=%Vrc\n", Port, *pu32Value, cbValue, rc));
805 return rc;
806 }
807
808#ifndef IN_RING3
809 /*
810 * Handler in ring-3?
811 */
812 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeHC(&pVM->iom.s, Port);
813 if (pRangeR3)
814 {
815# ifdef VBOX_WITH_STATISTICS
816 if (pStats)
817 STAM_COUNTER_INC(&pStats->CTXALLMID(In,ToR3));
818# endif
819 return VINF_IOM_HC_IOPORT_READ;
820 }
821#endif
822
823 /*
824 * Ok, no handler for this port.
825 */
826#ifdef VBOX_WITH_STATISTICS
827 if (pStats)
828 STAM_COUNTER_INC(&pStats->CTXALLSUFF(In));
829 else
830 {
831# ifndef IN_RING3
832 /* Ring-3 will have to create the statistics record. */
833 return VINF_IOM_HC_IOPORT_READ;
834# else
835 pStats = iomr3IOPortStatsCreate(pVM, Port, NULL);
836 if (pStats)
837 STAM_COUNTER_INC(&pStats->CTXALLSUFF(In));
838# endif
839 }
840#endif
841
842 /* make return value */
843 switch (cbValue)
844 {
845 case 1: *(uint8_t *)pu32Value = 0xff; break;
846 case 2: *(uint16_t *)pu32Value = 0xffff; break;
847 case 4: *(uint32_t *)pu32Value = 0xffffffff; break;
848 default:
849 AssertMsgFailed(("Invalid I/O port size %d. Port=%d\n", cbValue, Port));
850 return VERR_IOM_INVALID_IOPORT_SIZE;
851 }
852 Log3(("IOMIOPortRead: Port=%RTiop *pu32=%08RX32 cb=%d rc=VINF_SUCCESS\n", Port, *pu32Value, cbValue));
853 return VINF_SUCCESS;
854}
855
856
857/**
858 * Reads the string buffer of an I/O port register.
859 *
860 * @returns Strict VBox status code. Informational status codes other than the one documented
861 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
862 * @retval VINF_SUCCESS Success.
863 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
864 * status code must be passed on to EM.
865 * @retval VINF_IOM_HC_IOPORT_READ Defer the read to ring-3. (R0/GC only)
866 *
867 * @param pVM VM handle.
868 * @param Port The port to read.
869 * @param pGCPtrDst Pointer to the destination buffer (GC, incremented appropriately).
870 * @param pcTransfers Pointer to the number of transfer units to read, on return remaining transfer units.
871 * @param cb Size of the transfer unit (1, 2 or 4 bytes).
872 * */
873IOMDECL(int) IOMIOPortReadString(PVM pVM, RTIOPORT Port, PRTGCPTR pGCPtrDst, PRTGCUINTREG pcTransfers, unsigned cb)
874{
875#ifdef LOG_ENABLED
876 const RTGCUINTREG cTransfers = *pcTransfers;
877#endif
878#ifdef VBOX_WITH_STATISTICS
879 /*
880 * Get the statistics record.
881 */
882 PIOMIOPORTSTATS pStats = CTXALLSUFF(pVM->iom.s.pStatsLastRead);
883 if (!pStats || pStats->Core.Key != Port)
884 {
885 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTXSUFF(pTrees)->IOPortStatTree, Port);
886 if (pStats)
887 CTXALLSUFF(pVM->iom.s.pStatsLastRead) = pStats;
888 }
889#endif
890
891 /*
892 * Get handler for current context.
893 */
894 CTXALLSUFF(PIOMIOPORTRANGE) pRange = CTXALLSUFF(pVM->iom.s.pRangeLastRead);
895 if ( !pRange
896 || (unsigned)Port - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
897 {
898 pRange = iomIOPortGetRange(&pVM->iom.s, Port);
899 if (pRange)
900 CTXALLSUFF(pVM->iom.s.pRangeLastRead) = pRange;
901 }
902#ifdef IN_GC
903 Assert(!pRange || MMHyperIsInsideArea(pVM, pRange)); /** @todo r=bird: there is a macro for this which skips the #if'ing. */
904#endif
905
906 if (pRange)
907 {
908 /*
909 * Found a range.
910 */
911#ifndef IN_RING3
912 if (!pRange->pfnInStrCallback)
913 {
914# ifdef VBOX_WITH_STATISTICS
915 if (pStats)
916 STAM_COUNTER_INC(&pStats->CTXALLMID(In,ToR3));
917# endif
918 return VINF_IOM_HC_IOPORT_READ;
919 }
920#endif
921 /* call the device. */
922#ifdef VBOX_WITH_STATISTICS
923 if (pStats)
924 STAM_PROFILE_ADV_START(&pStats->CTXALLSUFF(ProfIn), a);
925#endif
926
927 int rc = pRange->pfnInStrCallback(pRange->pDevIns, pRange->pvUser, Port, pGCPtrDst, pcTransfers, cb);
928#ifdef VBOX_WITH_STATISTICS
929 if (pStats)
930 STAM_PROFILE_ADV_STOP(&pStats->CTXALLSUFF(ProfIn), a);
931 if (rc == VINF_SUCCESS && pStats)
932 STAM_COUNTER_INC(&pStats->CTXALLSUFF(In));
933# ifndef IN_RING3
934 else if (rc == VINF_IOM_HC_IOPORT_READ && pStats)
935 STAM_COUNTER_INC(&pStats->CTXALLMID(In, ToR3));
936# endif
937#endif
938 Log3(("IOMIOPortReadStr: Port=%RTiop pGCPtrDst=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=%Vrc\n",
939 Port, pGCPtrDst, pcTransfers, cTransfers, *pcTransfers, cb, rc));
940 return rc;
941 }
942
943#ifndef IN_RING3
944 /*
945 * Handler in ring-3?
946 */
947 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeHC(&pVM->iom.s, Port);
948 if (pRangeR3)
949 {
950# ifdef VBOX_WITH_STATISTICS
951 if (pStats)
952 STAM_COUNTER_INC(&pStats->CTXALLMID(In,ToR3));
953# endif
954 return VINF_IOM_HC_IOPORT_READ;
955 }
956#endif
957
958 /*
959 * Ok, no handler for this port.
960 */
961#ifdef VBOX_WITH_STATISTICS
962 if (pStats)
963 STAM_COUNTER_INC(&pStats->CTXALLSUFF(In));
964 else
965 {
966# ifndef IN_RING3
967 /* Ring-3 will have to create the statistics record. */
968 return VINF_IOM_HC_IOPORT_READ;
969# else
970 pStats = iomr3IOPortStatsCreate(pVM, Port, NULL);
971 if (pStats)
972 STAM_COUNTER_INC(&pStats->CTXALLSUFF(In));
973# endif
974 }
975#endif
976
977 Log3(("IOMIOPortReadStr: Port=%RTiop pGCPtrDst=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=VINF_SUCCESS\n",
978 Port, pGCPtrDst, pcTransfers, cTransfers, *pcTransfers, cb));
979 return VINF_SUCCESS;
980}
981
982
983/**
984 * Writes to an I/O port register.
985 *
986 * @returns Strict VBox status code. Informational status codes other than the one documented
987 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
988 * @retval VINF_SUCCESS Success.
989 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
990 * status code must be passed on to EM.
991 * @retval VINF_IOM_HC_IOPORT_WRITE Defer the write to ring-3. (R0/GC only)
992 *
993 * @param pVM VM handle.
994 * @param Port The port to write to.
995 * @param u32Value The value to write.
996 * @param cbValue The size of the register to read in bytes. 1, 2 or 4 bytes.
997 */
998IOMDECL(int) IOMIOPortWrite(PVM pVM, RTIOPORT Port, uint32_t u32Value, size_t cbValue)
999{
1000/** @todo bird: When I get time, I'll remove the GC tree and link the GC entries to the ring-3 node. */
1001#ifdef VBOX_WITH_STATISTICS
1002 /*
1003 * Find the statistics record.
1004 */
1005 PIOMIOPORTSTATS pStats = CTXALLSUFF(pVM->iom.s.pStatsLastWrite);
1006 if (!pStats || pStats->Core.Key != Port)
1007 {
1008 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTXSUFF(pTrees)->IOPortStatTree, Port);
1009 if (pStats)
1010 CTXALLSUFF(pVM->iom.s.pStatsLastWrite) = pStats;
1011 }
1012#endif
1013
1014 /*
1015 * Get handler for current context.
1016 */
1017 CTXALLSUFF(PIOMIOPORTRANGE) pRange = CTXALLSUFF(pVM->iom.s.pRangeLastWrite);
1018 if ( !pRange
1019 || (unsigned)Port - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
1020 {
1021 pRange = iomIOPortGetRange(&pVM->iom.s, Port);
1022 if (pRange)
1023 CTXALLSUFF(pVM->iom.s.pRangeLastWrite) = pRange;
1024 }
1025#ifdef IN_GC
1026 Assert(!pRange || MMHyperIsInsideArea(pVM, pRange));
1027#endif
1028
1029 if (pRange)
1030 {
1031 /*
1032 * Found a range.
1033 */
1034#ifndef IN_RING3
1035 if (!pRange->pfnOutCallback)
1036 {
1037# ifdef VBOX_WITH_STATISTICS
1038 if (pStats)
1039 STAM_COUNTER_INC(&pStats->CTXALLMID(Out,ToR3));
1040# endif
1041 return VINF_IOM_HC_IOPORT_WRITE;
1042 }
1043#endif
1044 /* call the device. */
1045#ifdef VBOX_WITH_STATISTICS
1046 if (pStats)
1047 STAM_PROFILE_ADV_START(&pStats->CTXALLSUFF(ProfOut), a);
1048#endif
1049 int rc = pRange->pfnOutCallback(pRange->pDevIns, pRange->pvUser, Port, u32Value, cbValue);
1050
1051#ifdef VBOX_WITH_STATISTICS
1052 if (pStats)
1053 STAM_PROFILE_ADV_STOP(&pStats->CTXALLSUFF(ProfOut), a);
1054 if (rc == VINF_SUCCESS && pStats)
1055 STAM_COUNTER_INC(&pStats->CTXALLSUFF(Out));
1056# ifndef IN_RING3
1057 else if (rc == VINF_IOM_HC_IOPORT_WRITE && pStats)
1058 STAM_COUNTER_INC(&pStats->CTXALLMID(Out, ToR3));
1059# endif
1060#endif
1061 Log3(("IOMIOPortWrite: Port=%RTiop u32=%08RX32 cb=%d rc=%Vrc\n", Port, u32Value, cbValue, rc));
1062 return rc;
1063 }
1064
1065#ifndef IN_RING3
1066 /*
1067 * Handler in ring-3?
1068 */
1069 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeHC(&pVM->iom.s, Port);
1070 if (pRangeR3)
1071 {
1072# ifdef VBOX_WITH_STATISTICS
1073 if (pStats)
1074 STAM_COUNTER_INC(&pStats->CTXALLMID(Out,ToR3));
1075# endif
1076 return VINF_IOM_HC_IOPORT_WRITE;
1077 }
1078#endif
1079
1080 /*
1081 * Ok, no handler for that port.
1082 */
1083#ifdef VBOX_WITH_STATISTICS
1084 /* statistics. */
1085 if (pStats)
1086 STAM_COUNTER_INC(&pStats->CTXALLSUFF(Out));
1087 else
1088 {
1089# ifndef IN_RING3
1090 /* R3 will have to create the statistics record. */
1091 return VINF_IOM_HC_IOPORT_WRITE;
1092# else
1093 pStats = iomr3IOPortStatsCreate(pVM, Port, NULL);
1094 if (pStats)
1095 STAM_COUNTER_INC(&pStats->CTXALLSUFF(Out));
1096# endif
1097 }
1098#endif
1099 Log3(("IOMIOPortWrite: Port=%RTiop u32=%08RX32 cb=%d nop\n", Port, u32Value, cbValue));
1100 return VINF_SUCCESS;
1101}
1102
1103
1104/**
1105 * Writes the string buffer of an I/O port register.
1106 *
1107 * @returns Strict VBox status code. Informational status codes other than the one documented
1108 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
1109 * @retval VINF_SUCCESS Success.
1110 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
1111 * status code must be passed on to EM.
1112 * @retval VINF_IOM_HC_IOPORT_WRITE Defer the write to ring-3. (R0/GC only)
1113 *
1114 * @param pVM VM handle.
1115 * @param Port The port to write.
1116 * @param pGCPtrSrc Pointer to the source buffer (GC, incremented appropriately).
1117 * @param pcTransfers Pointer to the number of transfer units to write, on return remaining transfer units.
1118 * @param cb Size of the transfer unit (1, 2 or 4 bytes).
1119 * */
1120IOMDECL(int) IOMIOPortWriteString(PVM pVM, RTIOPORT Port, PRTGCPTR pGCPtrSrc, PRTGCUINTREG pcTransfers, unsigned cb)
1121{
1122#ifdef LOG_ENABLED
1123 const RTGCUINTREG cTransfers = *pcTransfers;
1124#endif
1125#ifdef VBOX_WITH_STATISTICS
1126 /*
1127 * Get the statistics record.
1128 */
1129 PIOMIOPORTSTATS pStats = CTXALLSUFF(pVM->iom.s.pStatsLastWrite);
1130 if (!pStats || pStats->Core.Key != Port)
1131 {
1132 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTXSUFF(pTrees)->IOPortStatTree, Port);
1133 if (pStats)
1134 CTXALLSUFF(pVM->iom.s.pStatsLastWrite) = pStats;
1135 }
1136#endif
1137
1138 /*
1139 * Get handler for current context.
1140 */
1141 CTXALLSUFF(PIOMIOPORTRANGE) pRange = CTXALLSUFF(pVM->iom.s.pRangeLastWrite);
1142 if ( !pRange
1143 || (unsigned)Port - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
1144 {
1145 pRange = iomIOPortGetRange(&pVM->iom.s, Port);
1146 if (pRange)
1147 CTXALLSUFF(pVM->iom.s.pRangeLastWrite) = pRange;
1148 }
1149#ifdef IN_GC
1150 Assert(!pRange || MMHyperIsInsideArea(pVM, pRange)); /** @todo r=bird: there is a macro for this which skips the #if'ing. */
1151#endif
1152
1153 if (pRange)
1154 {
1155 /*
1156 * Found a range.
1157 */
1158#ifndef IN_RING3
1159 if (!pRange->pfnOutStrCallback)
1160 {
1161# ifdef VBOX_WITH_STATISTICS
1162 if (pStats)
1163 STAM_COUNTER_INC(&pStats->CTXALLMID(Out,ToR3));
1164# endif
1165 return VINF_IOM_HC_IOPORT_WRITE;
1166 }
1167#endif
1168 /* call the device. */
1169#ifdef VBOX_WITH_STATISTICS
1170 if (pStats)
1171 STAM_PROFILE_ADV_START(&pStats->CTXALLSUFF(ProfOut), a);
1172#endif
1173 int rc = pRange->pfnOutStrCallback(pRange->pDevIns, pRange->pvUser, Port, pGCPtrSrc, pcTransfers, cb);
1174#ifdef VBOX_WITH_STATISTICS
1175 if (pStats)
1176 STAM_PROFILE_ADV_STOP(&pStats->CTXALLSUFF(ProfOut), a);
1177 if (rc == VINF_SUCCESS && pStats)
1178 STAM_COUNTER_INC(&pStats->CTXALLSUFF(Out));
1179# ifndef IN_RING3
1180 else if (rc == VINF_IOM_HC_IOPORT_WRITE && pStats)
1181 STAM_COUNTER_INC(&pStats->CTXALLMID(Out, ToR3));
1182# endif
1183#endif
1184 Log3(("IOMIOPortWriteStr: Port=%RTiop pGCPtrSrc=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=%Vrc\n",
1185 Port, pGCPtrSrc, pcTransfers, cTransfers, *pcTransfers, cb, rc));
1186 return rc;
1187 }
1188
1189#ifndef IN_RING3
1190 /*
1191 * Handler in ring-3?
1192 */
1193 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeHC(&pVM->iom.s, Port);
1194 if (pRangeR3)
1195 {
1196# ifdef VBOX_WITH_STATISTICS
1197 if (pStats)
1198 STAM_COUNTER_INC(&pStats->CTXALLMID(Out,ToR3));
1199# endif
1200 return VINF_IOM_HC_IOPORT_WRITE;
1201 }
1202#endif
1203
1204 /*
1205 * Ok, no handler for this port.
1206 */
1207#ifdef VBOX_WITH_STATISTICS
1208 if (pStats)
1209 STAM_COUNTER_INC(&pStats->CTXALLSUFF(Out));
1210 else
1211 {
1212# ifndef IN_RING3
1213 /* Ring-3 will have to create the statistics record. */
1214 return VINF_IOM_HC_IOPORT_WRITE;
1215# else
1216 pStats = iomr3IOPortStatsCreate(pVM, Port, NULL);
1217 if (pStats)
1218 STAM_COUNTER_INC(&pStats->CTXALLSUFF(Out));
1219# endif
1220 }
1221#endif
1222
1223 Log3(("IOMIOPortWriteStr: Port=%RTiop pGCPtrSrc=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=VINF_SUCCESS\n",
1224 Port, pGCPtrSrc, pcTransfers, cTransfers, *pcTransfers, cb));
1225 return VINF_SUCCESS;
1226}
1227
1228
1229/**
1230 * Checks that the operation is allowed according to the IOPL
1231 * level and I/O bitmap.
1232 *
1233 * @returns Strict VBox status code. Informational status codes other than the one documented
1234 * here are to be treated as internal failure.
1235 * @retval VINF_SUCCESS Success.
1236 * @retval VINF_EM_RAW_GUEST_TRAP The exception was left pending. (TRPMRaiseXcptErr)
1237 * @retval VINF_TRPM_XCPT_DISPATCHED The exception was raised and dispatched for raw-mode execution. (TRPMRaiseXcptErr)
1238 * @retval VINF_EM_RESCHEDULE_REM The exception was dispatched and cannot be executed in raw-mode. (TRPMRaiseXcptErr)
1239 *
1240 * @param pVM VM handle.
1241 * @param pCtxCore Pointer to register frame.
1242 * @param Port The I/O port number.
1243 * @param cb The access size.
1244 */
1245IOMDECL(int) IOMInterpretCheckPortIOAccess(PVM pVM, PCPUMCTXCORE pCtxCore, RTIOPORT Port, unsigned cb)
1246{
1247 /*
1248 * If this isn't ring-0, we have to check for I/O privileges.
1249 */
1250 uint32_t efl = CPUMRawGetEFlags(pVM, pCtxCore);
1251 uint32_t cpl = CPUMGetGuestCPL(pVM, pCtxCore);
1252
1253 if ( ( cpl > 0
1254 && X86_EFL_GET_IOPL(efl) < cpl)
1255 || pCtxCore->eflags.Bits.u1VM /* IOPL is ignored in V86 mode; always check TSS bitmap */
1256 )
1257 {
1258 /*
1259 * Get TSS location and check if there can be a I/O bitmap.
1260 */
1261 RTGCUINTPTR GCPtrTss;
1262 RTGCUINTPTR cbTss;
1263 bool fCanHaveIOBitmap;
1264 int rc = SELMGetTSSInfo(pVM, &GCPtrTss, &cbTss, &fCanHaveIOBitmap);
1265 if (VBOX_FAILURE(rc))
1266 {
1267 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d %Vrc -> #GP(0)\n", Port, cb, rc));
1268 return TRPMRaiseXcptErr(pVM, pCtxCore, X86_XCPT_GP, 0);
1269 }
1270
1271 if ( !fCanHaveIOBitmap
1272 || cbTss <= sizeof(VBOXTSS))
1273 {
1274 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d cbTss=%#x fCanHaveIOBitmap=%RTbool -> #GP(0)\n",
1275 Port, cb, cbTss, fCanHaveIOBitmap));
1276 return TRPMRaiseXcptErr(pVM, pCtxCore, X86_XCPT_GP, 0);
1277 }
1278
1279 /*
1280 * Fetch the I/O bitmap offset.
1281 */
1282 uint16_t offIOPB;
1283 rc = PGMPhysInterpretedRead(pVM, pCtxCore, &offIOPB, GCPtrTss + RT_OFFSETOF(VBOXTSS, offIoBitmap), sizeof(offIOPB));
1284 if (rc != VINF_SUCCESS)
1285 {
1286 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d GCPtrTss=%VGv %Vrc\n",
1287 Port, cb, GCPtrTss, rc));
1288 return rc;
1289 }
1290
1291 /*
1292 * Check the limit and read the two bitmap bytes.
1293 */
1294 uint32_t offTss = offIOPB + (Port >> 3);
1295 if (offTss + 1 >= cbTss)
1296 {
1297 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d offTss=%#x cbTss=%#x -> #GP(0)\n",
1298 Port, cb, offTss, cbTss));
1299 return TRPMRaiseXcptErr(pVM, pCtxCore, X86_XCPT_GP, 0);
1300 }
1301 uint16_t u16;
1302 rc = PGMPhysInterpretedRead(pVM, pCtxCore, &u16, GCPtrTss + offTss, sizeof(u16));
1303 if (rc != VINF_SUCCESS)
1304 {
1305 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d GCPtrTss=%VGv offTss=%#x -> %Vrc\n",
1306 Port, cb, GCPtrTss, offTss, rc));
1307 return rc;
1308 }
1309
1310 /*
1311 * All the bits must be clear.
1312 */
1313 if ((u16 >> (Port & 7)) & ((1 << cb) - 1))
1314 {
1315 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d u16=%#x -> #GP(0)\n",
1316 Port, cb, u16, offTss));
1317 return TRPMRaiseXcptErr(pVM, pCtxCore, X86_XCPT_GP, 0);
1318 }
1319 LogFlow(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d offTss=%#x cbTss=%#x u16=%#x -> OK\n",
1320 Port, cb, u16, offTss, cbTss));
1321 }
1322 return VINF_SUCCESS;
1323}
1324
1325/**
1326 * IN <AL|AX|EAX>, <DX|imm16>
1327 *
1328 * @returns Strict VBox status code. Informational status codes other than the one documented
1329 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
1330 * @retval VINF_SUCCESS Success.
1331 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
1332 * status code must be passed on to EM.
1333 * @retval VINF_IOM_HC_IOPORT_READ Defer the read to ring-3. (R0/GC only)
1334 * @retval VINF_EM_RAW_GUEST_TRAP The exception was left pending. (TRPMRaiseXcptErr)
1335 * @retval VINF_TRPM_XCPT_DISPATCHED The exception was raised and dispatched for raw-mode execution. (TRPMRaiseXcptErr)
1336 * @retval VINF_EM_RESCHEDULE_REM The exception was dispatched and cannot be executed in raw-mode. (TRPMRaiseXcptErr)
1337 *
1338 * @param pVM The virtual machine (GC pointer ofcourse).
1339 * @param pRegFrame Pointer to CPUMCTXCORE guest registers structure.
1340 * @param pCpu Disassembler CPU state.
1341 */
1342IOMDECL(int) IOMInterpretIN(PVM pVM, PCPUMCTXCORE pRegFrame, PDISCPUSTATE pCpu)
1343{
1344#ifdef IN_GC
1345 STAM_COUNTER_INC(&pVM->iom.s.StatGCInstIn);
1346#endif
1347
1348 /*
1349 * Get port number from second parameter.
1350 * And get the register size from the first parameter.
1351 */
1352 uint32_t uPort = 0;
1353 unsigned cbSize = 0;
1354 bool fRc = iomGCGetRegImmData(pCpu, &pCpu->param2, pRegFrame, &uPort, &cbSize);
1355 AssertMsg(fRc, ("Failed to get reg/imm port number!\n")); NOREF(fRc);
1356
1357 cbSize = iomGCGetRegSize(pCpu, &pCpu->param1);
1358 Assert(cbSize > 0);
1359 int rc = IOMInterpretCheckPortIOAccess(pVM, pRegFrame, uPort, cbSize);
1360 if (rc == VINF_SUCCESS)
1361 {
1362 /*
1363 * Attemp to read the port.
1364 */
1365 uint32_t u32Data = ~0U;
1366 rc = IOMIOPortRead(pVM, uPort, &u32Data, cbSize);
1367 if (IOM_SUCCESS(rc))
1368 {
1369 /*
1370 * Store the result in the AL|AX|EAX register.
1371 */
1372 fRc = iomGCSaveDataToReg(pCpu, &pCpu->param1, pRegFrame, u32Data);
1373 AssertMsg(fRc, ("Failed to store register value!\n")); NOREF(fRc);
1374 }
1375 else
1376 AssertMsg(rc == VINF_IOM_HC_IOPORT_READ || VBOX_FAILURE(rc), ("%Vrc\n", rc));
1377 }
1378 else
1379 AssertMsg(rc == VINF_EM_RAW_GUEST_TRAP || rc == VINF_TRPM_XCPT_DISPATCHED || rc == VINF_TRPM_XCPT_DISPATCHED || VBOX_FAILURE(rc), ("%Vrc\n", rc));
1380 return rc;
1381}
1382
1383
1384/**
1385 * OUT <DX|imm16>, <AL|AX|EAX>
1386 *
1387 * @returns Strict VBox status code. Informational status codes other than the one documented
1388 * here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
1389 * @retval VINF_SUCCESS Success.
1390 * @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
1391 * status code must be passed on to EM.
1392 * @retval VINF_IOM_HC_IOPORT_WRITE Defer the write to ring-3. (R0/GC only)
1393 * @retval VINF_EM_RAW_GUEST_TRAP The exception was left pending. (TRPMRaiseXcptErr)
1394 * @retval VINF_TRPM_XCPT_DISPATCHED The exception was raised and dispatched for raw-mode execution. (TRPMRaiseXcptErr)
1395 * @retval VINF_EM_RESCHEDULE_REM The exception was dispatched and cannot be executed in raw-mode. (TRPMRaiseXcptErr)
1396 *
1397 * @param pVM The virtual machine (GC pointer ofcourse).
1398 * @param pRegFrame Pointer to CPUMCTXCORE guest registers structure.
1399 * @param pCpu Disassembler CPU state.
1400 */
1401IOMDECL(int) IOMInterpretOUT(PVM pVM, PCPUMCTXCORE pRegFrame, PDISCPUSTATE pCpu)
1402{
1403#ifdef IN_GC
1404 STAM_COUNTER_INC(&pVM->iom.s.StatGCInstOut);
1405#endif
1406
1407 /*
1408 * Get port number from first parameter.
1409 * And get the register size and value from the second parameter.
1410 */
1411 uint32_t uPort = 0;
1412 unsigned cbSize = 0;
1413 bool fRc = iomGCGetRegImmData(pCpu, &pCpu->param1, pRegFrame, &uPort, &cbSize);
1414 AssertMsg(fRc, ("Failed to get reg/imm port number!\n")); NOREF(fRc);
1415
1416 int rc = IOMInterpretCheckPortIOAccess(pVM, pRegFrame, uPort, cbSize);
1417 if (rc == VINF_SUCCESS)
1418 {
1419 uint32_t u32Data = 0;
1420 fRc = iomGCGetRegImmData(pCpu, &pCpu->param2, pRegFrame, &u32Data, &cbSize);
1421 AssertMsg(fRc, ("Failed to get reg value!\n")); NOREF(fRc);
1422
1423 /*
1424 * Attempt to write to the port.
1425 */
1426 rc = IOMIOPortWrite(pVM, uPort, u32Data, cbSize);
1427 AssertMsg(rc == VINF_SUCCESS || rc == VINF_IOM_HC_IOPORT_WRITE || (rc >= VINF_EM_FIRST && rc <= VINF_EM_LAST) || VBOX_FAILURE(rc), ("%Vrc\n", rc));
1428 }
1429 else
1430 AssertMsg(rc == VINF_EM_RAW_GUEST_TRAP || rc == VINF_TRPM_XCPT_DISPATCHED || rc == VINF_TRPM_XCPT_DISPATCHED || VBOX_FAILURE(rc), ("%Vrc\n", rc));
1431 return rc;
1432}
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