VirtualBox

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

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

Documented port I/O status code and fixed places where we didn't handle them correctly. (part 1)

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