VirtualBox

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

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

Profile MMIO as well

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 43.5 KB
Line 
1/* $Id: IOMAll.cpp 708 2007-02-06 14:15:26Z 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 * Registers a Port IO GC handler.
42 *
43 * This API is called by PDM on behalf of a device. Devices must first register ring-3 ranges
44 * using IOMIOPortRegisterR3() before calling this function.
45 *
46 *
47 * @returns VBox status code.
48 *
49 * @param pVM VM handle.
50 * @param pDevIns PDM device instance owning the port range.
51 * @param PortStart First port number in the range.
52 * @param cPorts Number of ports to register.
53 * @param pvUser User argument for the callbacks.
54 * @param pfnOutCallback Pointer to function which is gonna handle OUT operations in GC.
55 * @param pfnInCallback Pointer to function which is gonna handle IN operations in GC.
56 * @param pfnOutStrCallback Pointer to function which is gonna handle string OUT operations in GC.
57 * @param pfnInStrCallback Pointer to function which is gonna handle string IN operations in GC.
58 * @param pszDesc Pointer to description string. This must not be freed.
59 */
60IOMDECL(int) IOMIOPortRegisterGC(PVM pVM, PPDMDEVINS pDevIns, RTIOPORT PortStart, RTUINT cPorts, RTGCPTR pvUser,
61 GCPTRTYPE(PFNIOMIOPORTOUT) pfnOutCallback, GCPTRTYPE(PFNIOMIOPORTIN) pfnInCallback,
62 GCPTRTYPE(PFNIOMIOPORTOUTSTRING) pfnOutStrCallback, GCPTRTYPE(PFNIOMIOPORTINSTRING) pfnInStrCallback, const char *pszDesc)
63{
64 LogFlow(("IOMIOPortRegisterGC: pDevIns=%p PortStart=%#x cPorts=%#x pvUser=%VGv pfnOutCallback=%VGv pfnInCallback=%VGv pfnOutStrCallback=%VGv pfnInStrCallback=%VGv pszDesc=%s\n",
65 pDevIns, PortStart, cPorts, pvUser, pfnOutCallback, pfnInCallback, pfnOutStrCallback, pfnInStrCallback, pszDesc));
66
67 /*
68 * Validate input.
69 */
70 if ( (RTUINT)PortStart + cPorts <= (RTUINT)PortStart
71 || (RTUINT)PortStart + cPorts > 0x10000)
72 {
73 AssertMsgFailed(("Invalid port range %#x-%#x! (%s)\n", PortStart, (RTUINT)PortStart + (cPorts - 1), pszDesc));
74 return VERR_IOM_INVALID_IOPORT_RANGE;
75 }
76 RTIOPORT PortLast = PortStart + (cPorts - 1);
77 if (!pfnOutCallback && !pfnInCallback)
78 {
79 AssertMsgFailed(("Invalid port range %#x-%#x! No callbacks! (%s)\n", PortStart, PortLast, pszDesc));
80 return VERR_INVALID_PARAMETER;
81 }
82
83 /*
84 * Validate that there are ring-3 ranges for the ports.
85 */
86 RTIOPORT Port = PortStart;
87 while (Port <= PortLast && Port >= PortStart)
88 {
89 PIOMIOPORTRANGER3 pRange = (PIOMIOPORTRANGER3)RTAvlroIOPortRangeGet(&pVM->iom.s.CTXSUFF(pTrees)->IOPortTreeR3, Port);
90 if (!pRange)
91 {
92 AssertMsgFailed(("No R3! Port=#x %#x-%#x! (%s)\n", Port, PortStart, (unsigned)PortStart + cPorts - 1, pszDesc));
93 return VERR_IOM_NO_HC_IOPORT_RANGE;
94 }
95#ifndef IOM_NO_PDMINS_CHECKS
96 #ifndef IN_GC
97 if (pRange->pDevIns != pDevIns)
98 #else
99 if (pRange->pDevIns != MMHyperGC2HC(pVM, pDevIns))
100 #endif
101 {
102 AssertMsgFailed(("Not owner! Port=%#x %#x-%#x! (%s)\n", Port, PortStart, (unsigned)PortStart + cPorts - 1, pszDesc));
103 return VERR_IOM_NOT_IOPORT_RANGE_OWNER;
104 }
105#endif
106 Port = pRange->Core.KeyLast + 1;
107 }
108
109 /* Flush the IO port lookup cache */
110 IOMFlushCache(pVM);
111
112 /*
113 * Allocate new range record and initialize it.
114 */
115 PIOMIOPORTRANGEGC pRange;
116 int rc = MMHyperAlloc(pVM, sizeof(*pRange), 0, MM_TAG_IOM, (void **)&pRange);
117 if (VBOX_SUCCESS(rc))
118 {
119 pRange->Core.Key = PortStart;
120 pRange->Core.KeyLast = PortLast;
121 pRange->Port = PortStart;
122 pRange->cPorts = cPorts;
123 pRange->pvUser = pvUser;
124 pRange->pfnOutCallback = pfnOutCallback;
125 pRange->pfnInCallback = pfnInCallback;
126 pRange->pfnOutStrCallback = pfnOutStrCallback;
127 pRange->pfnInStrCallback = pfnInStrCallback;
128 #ifdef IN_GC
129 pRange->pDevIns = pDevIns;
130 pRange->pszDesc = MMHyperGC2HC(pVM, (void *)pszDesc);
131 #else
132 pRange->pDevIns = MMHyperHC2GC(pVM, pDevIns);
133 pRange->pszDesc = pszDesc;
134 #endif
135
136 /*
137 * Insert it.
138 */
139 if (RTAvlroIOPortInsert(&pVM->iom.s.CTXSUFF(pTrees)->IOPortTreeGC, &pRange->Core))
140 return VINF_SUCCESS;
141
142 /* conflict. */
143 AssertMsgFailed(("Port range %#x-%#x (%s) conflicts with existing range(s)!\n", PortStart, (unsigned)PortStart + cPorts - 1, pszDesc));
144 MMHyperFree(pVM, pRange);
145 rc = VERR_IOM_IOPORT_RANGE_CONFLICT;
146 }
147
148 return rc;
149}
150
151
152/**
153 * Registers a Memory Mapped I/O GC handler range.
154 *
155 * This API is called by PDM on behalf of a device. Devices must first register ring-3 ranges
156 * using IOMMMIORegisterR3() before calling this function.
157 *
158 *
159 * @returns VBox status code.
160 *
161 * @param pVM VM handle.
162 * @param pDevIns PDM device instance owning the MMIO range.
163 * @param GCPhysStart First physical address in the range.
164 * @param cbRange The size of the range (in bytes).
165 * @param pvUser User argument for the callbacks.
166 * @param pfnWriteCallback Pointer to function which is gonna handle Write operations.
167 * @param pfnReadCallback Pointer to function which is gonna handle Read operations.
168 * @param pfnFillCallback Pointer to function which is gonna handle Fill/memset operations.
169 * @param pszDesc Pointer to description string. This must not be freed.
170 */
171IOMDECL(int) IOMMMIORegisterGC(PVM pVM, PPDMDEVINS pDevIns, RTGCPHYS GCPhysStart, RTUINT cbRange, RTGCPTR pvUser,
172 GCPTRTYPE(PFNIOMMMIOWRITE) pfnWriteCallback, GCPTRTYPE(PFNIOMMMIOREAD) pfnReadCallback,
173 GCPTRTYPE(PFNIOMMMIOFILL) pfnFillCallback, const char *pszDesc)
174{
175 LogFlow(("IOMMMIORegisterGC: pDevIns=%p GCPhysStart=%#x cbRange=%#x pvUser=%VGv pfnWriteCallback=%#x pfnReadCallback=%#x pfnFillCallback=%#x pszDesc=%s\n",
176 pDevIns, GCPhysStart, cbRange, pvUser, pfnWriteCallback, pfnReadCallback, pfnFillCallback, pszDesc));
177
178 /*
179 * Validate input.
180 */
181 if (!pfnWriteCallback && !pfnReadCallback)
182 {
183 AssertMsgFailed(("No callbacks! %#x LB%#x %s\n", GCPhysStart, cbRange, pszDesc));
184 return VERR_INVALID_PARAMETER;
185 }
186 RTGCPHYS GCPhysLast = GCPhysStart + (cbRange - 1);
187 if (GCPhysLast < GCPhysStart)
188 {
189 AssertMsgFailed(("Wrapped! %#x LB%#x %s\n", GCPhysStart, cbRange, pszDesc));
190 return VERR_IOM_INVALID_MMIO_RANGE;
191 }
192
193 /*
194 * Check that a ring-3 MMIO range exists.
195 */
196 RTGCPHYS GCPhys = GCPhysStart;
197 while (GCPhys <= GCPhysLast && GCPhys >= GCPhysStart)
198 {
199 PIOMMMIORANGER3 pRange = (PIOMMMIORANGER3)RTAvlroGCPhysRangeGet(&pVM->iom.s.CTXSUFF(pTrees)->MMIOTreeR3, GCPhys);
200 if (!pRange)
201 {
202 AssertMsgFailed(("No R3 range! GCPhys=%#x %#x LB%#x %s\n", GCPhys, GCPhysStart, cbRange, pszDesc));
203 return VERR_IOM_NO_HC_MMIO_RANGE;
204 }
205#ifndef IOM_NO_PDMINS_CHECKS
206 #ifndef IN_GC
207 if (pRange->pDevIns != pDevIns)
208 #else
209 if (pRange->pDevIns != MMHyperGC2HC(pVM, pDevIns))
210 #endif
211 {
212 AssertMsgFailed(("Not owner! GCPhys=%#x %#x LB%#x %s / %#x-%#x %s\n", GCPhys, GCPhysStart, cbRange, pszDesc,
213 pRange->Core.Key, pRange->Core.KeyLast, MMHyper2HC(pVM, (uintptr_t)pRange->pszDesc)));
214 return VERR_IOM_NOT_MMIO_RANGE_OWNER;
215 }
216#endif /* !IOM_NO_PDMINS_CHECKS */
217 /* next */
218 Assert(GCPhys <= pRange->Core.KeyLast);
219 GCPhys = pRange->Core.KeyLast + 1;
220 }
221
222
223 /*
224 * Allocate new range record and initialize it.
225 */
226 PIOMMMIORANGEGC pRange;
227 int rc = MMHyperAlloc(pVM, sizeof(*pRange), 0, MM_TAG_IOM, (void **)&pRange);
228 if (VBOX_SUCCESS(rc))
229 {
230 pRange->Core.Key = GCPhysStart;
231 pRange->Core.KeyLast = GCPhysStart + (cbRange - 1);
232 pRange->GCPhys = GCPhysStart;
233 pRange->cbSize = cbRange;
234 pRange->pvUser = pvUser;
235 pRange->pfnReadCallback = pfnReadCallback;
236 pRange->pfnWriteCallback= pfnWriteCallback;
237 pRange->pfnFillCallback = pfnFillCallback;
238 #ifdef IN_GC
239 pRange->pDevIns = pDevIns;
240 pRange->pszDesc = MMHyperGC2HC(pVM, (void *)pszDesc);
241 #else
242 pRange->pDevIns = MMHyperHC2GC(pVM, pDevIns);
243 pRange->pszDesc = pszDesc;
244 #endif
245
246 /*
247 * Try insert it.
248 */
249 if (RTAvlroGCPhysInsert(&pVM->iom.s.CTXSUFF(pTrees)->MMIOTreeGC, &pRange->Core))
250 return VINF_SUCCESS;
251
252 AssertMsgFailed(("Conflict! %#x LB%#x %s\n", GCPhysStart, cbRange, pszDesc));
253 MMHyperFree(pVM, pRange);
254 rc = VERR_IOM_MMIO_RANGE_CONFLICT;
255 }
256
257 return rc;
258}
259
260
261/**
262 * Registers a Port IO R0 handler.
263 *
264 * This API is called by PDM on behalf of a device. Devices must first register ring-3 ranges
265 * using IOMR3IOPortRegisterR3() before calling this function.
266 *
267 *
268 * @returns VBox status code.
269 *
270 * @param pVM VM handle.
271 * @param pDevIns PDM device instance owning the port range.
272 * @param PortStart First port number in the range.
273 * @param cPorts Number of ports to register.
274 * @param pvUser User argument for the callbacks.
275 * @param pfnOutCallback Pointer to function which is gonna handle OUT operations in GC.
276 * @param pfnInCallback Pointer to function which is gonna handle IN operations in GC.
277 * @param pfnOutStrCallback Pointer to function which is gonna handle OUT operations in GC.
278 * @param pfnInStrCallback Pointer to function which is gonna handle IN operations in GC.
279 * @param pszDesc Pointer to description string. This must not be freed.
280 */
281IOMDECL(int) IOMIOPortRegisterR0(PVM pVM, PPDMDEVINS pDevIns, RTIOPORT PortStart, RTUINT cPorts, RTHCPTR pvUser,
282 HCPTRTYPE(PFNIOMIOPORTOUT) pfnOutCallback, HCPTRTYPE(PFNIOMIOPORTIN) pfnInCallback,
283 HCPTRTYPE(PFNIOMIOPORTOUTSTRING) pfnOutStrCallback, HCPTRTYPE(PFNIOMIOPORTINSTRING) pfnInStrCallback,
284 const char *pszDesc)
285{
286 LogFlow(("IOMIOPortRegisterR0: pDevIns=%p PortStart=%#x cPorts=%#x pvUser=%VHv pfnOutCallback=%VGv pfnInCallback=%VGv pfnOutStrCallback=%VGv pfnInStrCallback=%VGv pszDesc=%s\n",
287 pDevIns, PortStart, cPorts, pvUser, pfnOutCallback, pfnInCallback, pfnOutStrCallback, pfnInStrCallback, pszDesc));
288
289 /*
290 * Validate input.
291 */
292 if ( (RTUINT)PortStart + cPorts <= (RTUINT)PortStart
293 || (RTUINT)PortStart + cPorts > 0x10000)
294 {
295 AssertMsgFailed(("Invalid port range %#x-%#x! (%s)\n", PortStart, (RTUINT)PortStart + (cPorts - 1), pszDesc));
296 return VERR_IOM_INVALID_IOPORT_RANGE;
297 }
298 RTIOPORT PortLast = PortStart + (cPorts - 1);
299 if (!pfnOutCallback && !pfnInCallback)
300 {
301 AssertMsgFailed(("Invalid port range %#x-%#x! No callbacks! (%s)\n", PortStart, PortLast, pszDesc));
302 return VERR_INVALID_PARAMETER;
303 }
304
305 /*
306 * Validate that there are ring-3 ranges for the ports.
307 */
308 RTIOPORT Port = PortStart;
309 while (Port <= PortLast && Port >= PortStart)
310 {
311 PIOMIOPORTRANGER3 pRange = (PIOMIOPORTRANGER3)RTAvlroIOPortRangeGet(&pVM->iom.s.CTXSUFF(pTrees)->IOPortTreeR3, Port);
312 if (!pRange)
313 {
314 AssertMsgFailed(("No R3! Port=#x %#x-%#x! (%s)\n", Port, PortStart, (unsigned)PortStart + cPorts - 1, pszDesc));
315 return VERR_IOM_NO_HC_IOPORT_RANGE;
316 }
317#ifndef IOM_NO_PDMINS_CHECKS
318# ifndef IN_GC
319 if (pRange->pDevIns != pDevIns)
320# else
321 if (pRange->pDevIns != MMHyperGC2HC(pVM, pDevIns))
322# endif
323 {
324 AssertMsgFailed(("Not owner! Port=%#x %#x-%#x! (%s)\n", Port, PortStart, (unsigned)PortStart + cPorts - 1, pszDesc));
325 return VERR_IOM_NOT_IOPORT_RANGE_OWNER;
326 }
327#endif
328 Port = pRange->Core.KeyLast + 1;
329 }
330
331 /* Flush the IO port lookup cache */
332 IOMFlushCache(pVM);
333
334 /*
335 * Allocate new range record and initialize it.
336 */
337 PIOMIOPORTRANGER0 pRange;
338 int rc = MMHyperAlloc(pVM, sizeof(*pRange), 0, MM_TAG_IOM, (void **)&pRange);
339 if (VBOX_SUCCESS(rc))
340 {
341 pRange->Core.Key = PortStart;
342 pRange->Core.KeyLast = PortLast;
343 pRange->Port = PortStart;
344 pRange->cPorts = cPorts;
345 pRange->pvUser = pvUser;
346 pRange->pfnOutCallback = pfnOutCallback;
347 pRange->pfnInCallback = pfnInCallback;
348 pRange->pfnOutStrCallback = pfnOutStrCallback;
349 pRange->pfnInStrCallback = pfnInStrCallback;
350#ifdef IN_GC
351 pRange->pDevIns = MMHyperGC2HC(pVM, pDevIns);
352 pRange->pszDesc = MMHyperGC2HC(pVM, (void *)pszDesc);
353#else
354 pRange->pDevIns = pDevIns;
355 pRange->pszDesc = pszDesc;
356#endif
357
358 /*
359 * Insert it.
360 */
361 if (RTAvlroIOPortInsert(&pVM->iom.s.CTXSUFF(pTrees)->IOPortTreeR0, &pRange->Core))
362 return VINF_SUCCESS;
363
364 /* conflict. */
365 AssertMsgFailed(("Port range %#x-%#x (%s) conflicts with existing range(s)!\n", PortStart, (unsigned)PortStart + cPorts - 1, pszDesc));
366 MMHyperFree(pVM, pRange);
367 rc = VERR_IOM_IOPORT_RANGE_CONFLICT;
368 }
369
370 return rc;
371}
372
373
374/**
375 * Registers a Memory Mapped I/O R0 handler range.
376 *
377 * This API is called by PDM on behalf of a device. Devices must first register ring-3 ranges
378 * using IOMMR3MIORegisterHC() before calling this function.
379 *
380 *
381 * @returns VBox status code.
382 *
383 * @param pVM VM handle.
384 * @param pDevIns PDM device instance owning the MMIO range.
385 * @param GCPhysStart First physical address in the range.
386 * @param cbRange The size of the range (in bytes).
387 * @param pvUser User argument for the callbacks.
388 * @param pfnWriteCallback Pointer to function which is gonna handle Write operations.
389 * @param pfnReadCallback Pointer to function which is gonna handle Read operations.
390 * @param pfnFillCallback Pointer to function which is gonna handle Fill/memset operations.
391 * @param pszDesc Pointer to description string. This must not be freed.
392 */
393IOMDECL(int) IOMMMIORegisterR0(PVM pVM, PPDMDEVINS pDevIns, RTGCPHYS GCPhysStart, RTUINT cbRange, RTHCPTR pvUser,
394 HCPTRTYPE(PFNIOMMMIOWRITE) pfnWriteCallback, HCPTRTYPE(PFNIOMMMIOREAD) pfnReadCallback,
395 HCPTRTYPE(PFNIOMMMIOFILL) pfnFillCallback, const char *pszDesc)
396{
397 LogFlow(("IOMMMIORegisterR0: pDevIns=%p GCPhysStart=%#x cbRange=%#x pvUser=%VHv pfnWriteCallback=%#x pfnReadCallback=%#x pfnFillCallback=%#x pszDesc=%s\n",
398 pDevIns, GCPhysStart, cbRange, pvUser, pfnWriteCallback, pfnReadCallback, pfnFillCallback, pszDesc));
399
400 /*
401 * Validate input.
402 */
403 if (!pfnWriteCallback && !pfnReadCallback)
404 {
405 AssertMsgFailed(("No callbacks! %#x LB%#x %s\n", GCPhysStart, cbRange, pszDesc));
406 return VERR_INVALID_PARAMETER;
407 }
408 RTGCPHYS GCPhysLast = GCPhysStart + (cbRange - 1);
409 if (GCPhysLast < GCPhysStart)
410 {
411 AssertMsgFailed(("Wrapped! %#x LB%#x %s\n", GCPhysStart, cbRange, pszDesc));
412 return VERR_IOM_INVALID_MMIO_RANGE;
413 }
414
415 /*
416 * Check that a ring-3 MMIO range exists.
417 */
418 RTGCPHYS GCPhys = GCPhysStart;
419 while (GCPhys <= GCPhysLast && GCPhys >= GCPhysStart)
420 {
421 PIOMMMIORANGER3 pRange = (PIOMMMIORANGER3)RTAvlroGCPhysRangeGet(&pVM->iom.s.CTXSUFF(pTrees)->MMIOTreeR3, GCPhys);
422 if (!pRange)
423 {
424 AssertMsgFailed(("No R3 range! GCPhys=%#x %#x LB%#x %s\n", GCPhys, GCPhysStart, cbRange, pszDesc));
425 return VERR_IOM_NO_HC_MMIO_RANGE;
426 }
427#ifndef IOM_NO_PDMINS_CHECKS
428# ifndef IN_GC
429 if (pRange->pDevIns != pDevIns)
430# else
431 if (pRange->pDevIns != MMHyperGC2HC(pVM, pDevIns))
432# endif
433 {
434 AssertMsgFailed(("Not owner! GCPhys=%#x %#x LB%#x %s / %#x-%#x %s\n", GCPhys, GCPhysStart, cbRange, pszDesc,
435 pRange->Core.Key, pRange->Core.KeyLast, MMHyper2HC(pVM, (uintptr_t)pRange->pszDesc)));
436 return VERR_IOM_NOT_MMIO_RANGE_OWNER;
437 }
438#endif /* !IOM_NO_PDMINS_CHECKS */
439 /* next */
440 Assert(GCPhys <= pRange->Core.KeyLast);
441 GCPhys = pRange->Core.KeyLast + 1;
442 }
443
444
445 /*
446 * Allocate new range record and initialize it.
447 */
448 PIOMMMIORANGER0 pRange;
449 int rc = MMHyperAlloc(pVM, sizeof(*pRange), 0, MM_TAG_IOM, (void **)&pRange);
450 if (VBOX_SUCCESS(rc))
451 {
452 pRange->Core.Key = GCPhysStart;
453 pRange->Core.KeyLast = GCPhysStart + (cbRange - 1);
454 pRange->GCPhys = GCPhysStart;
455 pRange->cbSize = cbRange;
456 pRange->pvUser = pvUser;
457 pRange->pfnReadCallback = pfnReadCallback;
458 pRange->pfnWriteCallback= pfnWriteCallback;
459 pRange->pfnFillCallback = pfnFillCallback;
460#ifdef IN_GC
461 pRange->pDevIns = MMHyperGC2HC(pVM, pDevIns);
462 pRange->pszDesc = MMHyperGC2HC(pVM, (void *)pszDesc);
463#else
464 pRange->pDevIns = pDevIns;
465 pRange->pszDesc = pszDesc;
466#endif
467
468 /*
469 * Try insert it.
470 */
471 if (RTAvlroGCPhysInsert(&pVM->iom.s.CTXSUFF(pTrees)->MMIOTreeR0, &pRange->Core))
472 return VINF_SUCCESS;
473
474 AssertMsgFailed(("Conflict! %#x LB%#x %s\n", GCPhysStart, cbRange, pszDesc));
475 MMHyperFree(pVM, pRange);
476 rc = VERR_IOM_MMIO_RANGE_CONFLICT;
477 }
478
479 return rc;
480}
481
482
483/**
484 * Flushes the IOM port & statistics lookup cache
485 *
486 * @param pVM The VM.
487 */
488IOMDECL(void) IOMFlushCache(PVM pVM)
489{
490 /*
491 * Caching of port and statistics (saves some time in rep outs/ins instruction emulation)
492 */
493 pVM->iom.s.pRangeLastReadGC = 0;
494 pVM->iom.s.pRangeLastWriteGC = 0;
495 pVM->iom.s.pStatsLastReadGC = 0;
496 pVM->iom.s.pStatsLastWriteGC = 0;
497
498 pVM->iom.s.pRangeLastReadR3 = 0;
499 pVM->iom.s.pRangeLastWriteR3 = 0;
500 pVM->iom.s.pStatsLastReadR3 = 0;
501 pVM->iom.s.pStatsLastWriteR3 = 0;
502
503 pVM->iom.s.pRangeLastReadR0 = 0;
504 pVM->iom.s.pRangeLastWriteR0 = 0;
505 pVM->iom.s.pStatsLastReadR0 = 0;
506 pVM->iom.s.pStatsLastWriteR0 = 0;
507}
508
509
510//#undef LOG_GROUP
511//#define LOG_GROUP LOG_GROUP_IOM_IOPORT
512
513/**
514 * Reads an I/O port register.
515 *
516 * @returns VBox status code.
517 *
518 * @param pVM VM handle.
519 * @param Port The port to read.
520 * @param pu32Value Where to store the value read.
521 * @param cbValue The size of the register to read in bytes. 1, 2 or 4 bytes.
522 */
523IOMDECL(int) IOMIOPortRead(PVM pVM, RTIOPORT Port, uint32_t *pu32Value, size_t cbValue)
524{
525#ifdef VBOX_WITH_STATISTICS
526 /*
527 * Get the statistics record.
528 */
529 PIOMIOPORTSTATS pStats = CTXALLSUFF(pVM->iom.s.pStatsLastRead);
530 if (!pStats || pStats->Core.Key != Port)
531 {
532 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTXSUFF(pTrees)->IOPortStatTree, Port);
533 if (pStats)
534 CTXALLSUFF(pVM->iom.s.pStatsLastRead) = pStats;
535 }
536#endif
537
538 /*
539 * Get handler for current context.
540 */
541 CTXALLSUFF(PIOMIOPORTRANGE) pRange = CTXALLSUFF(pVM->iom.s.pRangeLastRead);
542 if ( !pRange
543 || (unsigned)Port - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
544 {
545 pRange = iomIOPortGetRange(&pVM->iom.s, Port);
546 if (pRange)
547 CTXALLSUFF(pVM->iom.s.pRangeLastRead) = pRange;
548 }
549#ifdef IN_GC
550 Assert(!pRange || MMHyperIsInsideArea(pVM, pRange)); /** @todo r=bird: there is a macro for this which skips the #if'ing. */
551#endif
552
553 if (pRange)
554 {
555 /*
556 * Found a range.
557 */
558#ifndef IN_RING3
559 if (!pRange->pfnInCallback)
560 {
561# ifdef VBOX_WITH_STATISTICS
562 if (pStats)
563 STAM_COUNTER_INC(&pStats->CTXALLMID(In,ToR3));
564# endif
565 return VINF_IOM_HC_IOPORT_READ;
566 }
567#endif
568 /* call the device. */
569#ifdef VBOX_WITH_STATISTICS
570 if (pStats)
571 STAM_PROFILE_ADV_START(&pStats->CTXALLSUFF(ProfIn), a);
572#endif
573 int rc = pRange->pfnInCallback(pRange->pDevIns, pRange->pvUser, Port, pu32Value, cbValue);
574#ifdef VBOX_WITH_STATISTICS
575 if (pStats)
576 STAM_PROFILE_ADV_STOP(&pStats->CTXALLSUFF(ProfIn), a);
577 if (rc == VINF_SUCCESS && pStats)
578 STAM_COUNTER_INC(&pStats->CTXALLSUFF(In));
579# ifndef IN_RING3
580 else if (rc == VINF_IOM_HC_IOPORT_READ && pStats)
581 STAM_COUNTER_INC(&pStats->CTXALLMID(In,ToR3));
582# endif
583#endif
584 if (rc == VERR_IOM_IOPORT_UNUSED)
585 {
586 /* make return value */
587 rc = VINF_SUCCESS;
588 switch (cbValue)
589 {
590 case 1: *(uint8_t *)pu32Value = 0xff; break;
591 case 2: *(uint16_t *)pu32Value = 0xffff; break;
592 case 4: *(uint32_t *)pu32Value = 0xffffffff; break;
593 default:
594 AssertMsgFailed(("Invalid I/O port size %d. Port=%d\n", cbValue, Port));
595 return VERR_IOM_INVALID_IOPORT_SIZE;
596 }
597 }
598 Log3(("IOMIOPortRead: Port=%RTiop *pu32=%08RX32 cb=%d rc=%Vrc\n", Port, *pu32Value, cbValue, rc));
599 return rc;
600 }
601
602#ifndef IN_RING3
603 /*
604 * Handler in ring-3?
605 */
606 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeHC(&pVM->iom.s, Port);
607 if (pRangeR3)
608 {
609# ifdef VBOX_WITH_STATISTICS
610 if (pStats)
611 STAM_COUNTER_INC(&pStats->CTXALLMID(In,ToR3));
612# endif
613 return VINF_IOM_HC_IOPORT_READ;
614 }
615#endif
616
617 /*
618 * Ok, no handler for this port.
619 */
620#ifdef VBOX_WITH_STATISTICS
621 if (pStats)
622 STAM_COUNTER_INC(&pStats->CTXALLSUFF(In));
623 else
624 {
625# ifndef IN_RING3
626 /* Ring-3 will have to create the statistics record. */
627 return VINF_IOM_HC_IOPORT_READ;
628# else
629 pStats = iomr3IOPortStatsCreate(pVM, Port, NULL);
630 if (pStats)
631 STAM_COUNTER_INC(&pStats->CTXALLSUFF(In));
632# endif
633 }
634#endif
635
636 /* make return value */
637 switch (cbValue)
638 {
639 case 1: *(uint8_t *)pu32Value = 0xff; break;
640 case 2: *(uint16_t *)pu32Value = 0xffff; break;
641 case 4: *(uint32_t *)pu32Value = 0xffffffff; break;
642 default:
643 AssertMsgFailed(("Invalid I/O port size %d. Port=%d\n", cbValue, Port));
644 return VERR_IOM_INVALID_IOPORT_SIZE;
645 }
646 Log3(("IOMIOPortRead: Port=%RTiop *pu32=%08RX32 cb=%d rc=VINF_SUCCESS\n", Port, *pu32Value, cbValue));
647 return VINF_SUCCESS;
648}
649
650
651/**
652 * Reads the string buffer of an I/O port register.
653 *
654 * @returns VBox status code.
655 *
656 * @param pVM VM handle.
657 * @param Port The port to read.
658 * @param pGCPtrDst Pointer to the destination buffer (GC, incremented appropriately).
659 * @param pcTransfers Pointer to the number of transfer units to read, on return remaining transfer units.
660 * @param cb Size of the transfer unit (1, 2 or 4 bytes).
661 * */
662IOMDECL(int) IOMIOPortReadString(PVM pVM, RTIOPORT Port, PRTGCPTR pGCPtrDst, PRTGCUINTREG pcTransfers, unsigned cb)
663{
664#ifdef VBOX_WITH_STATISTICS
665 /*
666 * Get the statistics record.
667 */
668 RTGCUINTREG cTransfers = *pcTransfers;
669 PIOMIOPORTSTATS pStats = CTXALLSUFF(pVM->iom.s.pStatsLastRead);
670 if (!pStats || pStats->Core.Key != Port)
671 {
672 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTXSUFF(pTrees)->IOPortStatTree, Port);
673 if (pStats)
674 CTXALLSUFF(pVM->iom.s.pStatsLastRead) = pStats;
675 }
676#endif
677
678 /*
679 * Get handler for current context.
680 */
681 CTXALLSUFF(PIOMIOPORTRANGE) pRange = CTXALLSUFF(pVM->iom.s.pRangeLastRead);
682 if ( !pRange
683 || (unsigned)Port - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
684 {
685 pRange = iomIOPortGetRange(&pVM->iom.s, Port);
686 if (pRange)
687 CTXALLSUFF(pVM->iom.s.pRangeLastRead) = pRange;
688 }
689#ifdef IN_GC
690 Assert(!pRange || MMHyperIsInsideArea(pVM, pRange)); /** @todo r=bird: there is a macro for this which skips the #if'ing. */
691#endif
692
693 if (pRange)
694 {
695 /*
696 * Found a range.
697 */
698#ifndef IN_RING3
699 if (!pRange->pfnInStrCallback)
700 {
701# ifdef VBOX_WITH_STATISTICS
702 if (pStats)
703 STAM_COUNTER_INC(&pStats->CTXALLMID(In,ToR3));
704# endif
705 return VINF_IOM_HC_IOPORT_READ;
706 }
707#endif
708 /* call the device. */
709#ifdef VBOX_WITH_STATISTICS
710 if (pStats)
711 STAM_PROFILE_ADV_START(&pStats->CTXALLSUFF(ProfIn), a);
712#endif
713
714 int rc = pRange->pfnInStrCallback(pRange->pDevIns, pRange->pvUser, Port, pGCPtrDst, pcTransfers, cb);
715#ifdef VBOX_WITH_STATISTICS
716 if (pStats)
717 STAM_PROFILE_ADV_STOP(&pStats->CTXALLSUFF(ProfIn), a);
718 if (rc == VINF_SUCCESS && pStats)
719 STAM_COUNTER_INC(&pStats->CTXALLSUFF(In));
720# ifndef IN_RING3
721 else if (rc == VINF_IOM_HC_IOPORT_READ && pStats)
722 STAM_COUNTER_INC(&pStats->CTXALLMID(In, ToR3));
723# endif
724#endif
725 Log3(("IOMIOPortReadStr: Port=%RTiop pGCPtrDst=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=%Vrc\n",
726 Port, pGCPtrDst, pcTransfers, cTransfers, *pcTransfers, cb, rc));
727 return rc;
728 }
729
730#ifndef IN_RING3
731 /*
732 * Handler in ring-3?
733 */
734 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeHC(&pVM->iom.s, Port);
735 if (pRangeR3)
736 {
737# ifdef VBOX_WITH_STATISTICS
738 if (pStats)
739 STAM_COUNTER_INC(&pStats->CTXALLMID(In,ToR3));
740# endif
741 return VINF_IOM_HC_IOPORT_READ;
742 }
743#endif
744
745 /*
746 * Ok, no handler for this port.
747 */
748#ifdef VBOX_WITH_STATISTICS
749 if (pStats)
750 STAM_COUNTER_INC(&pStats->CTXALLSUFF(In));
751 else
752 {
753# ifndef IN_RING3
754 /* Ring-3 will have to create the statistics record. */
755 return VINF_IOM_HC_IOPORT_READ;
756# else
757 pStats = iomr3IOPortStatsCreate(pVM, Port, NULL);
758 if (pStats)
759 STAM_COUNTER_INC(&pStats->CTXALLSUFF(In));
760# endif
761 }
762#endif
763
764 Log3(("IOMIOPortReadStr: Port=%RTiop pGCPtrDst=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=VINF_SUCCESS\n",
765 Port, pGCPtrDst, pcTransfers, cTransfers, *pcTransfers, cb));
766 return VINF_SUCCESS;
767}
768
769
770/**
771 * Writes to an I/O port register.
772 *
773 * @returns VBox status code.
774 *
775 * @param pVM VM handle.
776 * @param Port The port to write to.
777 * @param u32Value The value to write.
778 * @param cbValue The size of the register to read in bytes. 1, 2 or 4 bytes.
779 */
780IOMDECL(int) IOMIOPortWrite(PVM pVM, RTIOPORT Port, uint32_t u32Value, size_t cbValue)
781{
782/** @todo bird: When I get time, I'll remove the GC tree and link the GC entries to the ring-3 node. */
783#ifdef VBOX_WITH_STATISTICS
784 /*
785 * Find the statistics record.
786 */
787 PIOMIOPORTSTATS pStats = CTXALLSUFF(pVM->iom.s.pStatsLastWrite);
788 if (!pStats || pStats->Core.Key != Port)
789 {
790 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTXSUFF(pTrees)->IOPortStatTree, Port);
791 if (pStats)
792 CTXALLSUFF(pVM->iom.s.pStatsLastWrite) = pStats;
793 }
794#endif
795
796 /*
797 * Get handler for current context.
798 */
799 CTXALLSUFF(PIOMIOPORTRANGE) pRange = CTXALLSUFF(pVM->iom.s.pRangeLastWrite);
800 if ( !pRange
801 || (unsigned)Port - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
802 {
803 pRange = iomIOPortGetRange(&pVM->iom.s, Port);
804 if (pRange)
805 CTXALLSUFF(pVM->iom.s.pRangeLastWrite) = pRange;
806 }
807#ifdef IN_GC
808 Assert(!pRange || MMHyperIsInsideArea(pVM, pRange));
809#endif
810
811 if (pRange)
812 {
813 /*
814 * Found a range.
815 */
816#ifndef IN_RING3
817 if (!pRange->pfnOutCallback)
818 {
819# ifdef VBOX_WITH_STATISTICS
820 if (pStats)
821 STAM_COUNTER_INC(&pStats->CTXALLMID(Out,ToR3));
822# endif
823 return VINF_IOM_HC_IOPORT_WRITE;
824 }
825#endif
826 /* call the device. */
827#ifdef VBOX_WITH_STATISTICS
828 if (pStats)
829 STAM_PROFILE_ADV_START(&pStats->CTXALLSUFF(ProfOut), a);
830#endif
831 int rc = pRange->pfnOutCallback(pRange->pDevIns, pRange->pvUser, Port, u32Value, cbValue);
832
833#ifdef VBOX_WITH_STATISTICS
834 if (pStats)
835 STAM_PROFILE_ADV_STOP(&pStats->CTXALLSUFF(ProfOut), a);
836 if (rc == VINF_SUCCESS && pStats)
837 STAM_COUNTER_INC(&pStats->CTXALLSUFF(Out));
838# ifndef IN_RING3
839 else if (rc == VINF_IOM_HC_IOPORT_WRITE && pStats)
840 STAM_COUNTER_INC(&pStats->CTXALLMID(Out, ToR3));
841# endif
842#endif
843 Log3(("IOMIOPortWrite: Port=%RTiop u32=%08RX32 cb=%d rc=%Vrc\n", Port, u32Value, cbValue, rc));
844 return rc;
845 }
846
847#ifndef IN_RING3
848 /*
849 * Handler in ring-3?
850 */
851 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeHC(&pVM->iom.s, Port);
852 if (pRangeR3)
853 {
854# ifdef VBOX_WITH_STATISTICS
855 if (pStats)
856 STAM_COUNTER_INC(&pStats->CTXALLMID(Out,ToR3));
857# endif
858 return VINF_IOM_HC_IOPORT_WRITE;
859 }
860#endif
861
862 /*
863 * Ok, no handler for that port.
864 */
865#ifdef VBOX_WITH_STATISTICS
866 /* statistics. */
867 if (pStats)
868 STAM_COUNTER_INC(&pStats->CTXALLSUFF(Out));
869 else
870 {
871# ifndef IN_RING3
872 /* R3 will have to create the statistics record. */
873 return VINF_IOM_HC_IOPORT_WRITE;
874# else
875 pStats = iomr3IOPortStatsCreate(pVM, Port, NULL);
876 if (pStats)
877 STAM_COUNTER_INC(&pStats->CTXALLSUFF(Out));
878# endif
879 }
880#endif
881 Log3(("IOMIOPortWrite: Port=%RTiop u32=%08RX32 cb=%d nop\n", Port, u32Value, cbValue));
882 return VINF_SUCCESS;
883}
884
885
886/**
887 * Writes the string buffer of an I/O port register.
888 *
889 * @returns VBox status code.
890 *
891 * @param pVM VM handle.
892 * @param Port The port to write.
893 * @param pGCPtrSrc Pointer to the source buffer (GC, incremented appropriately).
894 * @param pcTransfers Pointer to the number of transfer units to write, on return remaining transfer units.
895 * @param cb Size of the transfer unit (1, 2 or 4 bytes).
896 * */
897IOMDECL(int) IOMIOPortWriteString(PVM pVM, RTIOPORT Port, PRTGCPTR pGCPtrSrc, PRTGCUINTREG pcTransfers, unsigned cb)
898{
899#ifdef VBOX_WITH_STATISTICS
900 /*
901 * Get the statistics record.
902 */
903 const RTGCUINTREG cTransfers = *pcTransfers;
904 PIOMIOPORTSTATS pStats = CTXALLSUFF(pVM->iom.s.pStatsLastWrite);
905 if (!pStats || pStats->Core.Key != Port)
906 {
907 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTXSUFF(pTrees)->IOPortStatTree, Port);
908 if (pStats)
909 CTXALLSUFF(pVM->iom.s.pStatsLastWrite) = pStats;
910 }
911#endif
912
913 /*
914 * Get handler for current context.
915 */
916 CTXALLSUFF(PIOMIOPORTRANGE) pRange = CTXALLSUFF(pVM->iom.s.pRangeLastWrite);
917 if ( !pRange
918 || (unsigned)Port - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
919 {
920 pRange = iomIOPortGetRange(&pVM->iom.s, Port);
921 if (pRange)
922 CTXALLSUFF(pVM->iom.s.pRangeLastWrite) = pRange;
923 }
924#ifdef IN_GC
925 Assert(!pRange || MMHyperIsInsideArea(pVM, pRange)); /** @todo r=bird: there is a macro for this which skips the #if'ing. */
926#endif
927
928 if (pRange)
929 {
930 /*
931 * Found a range.
932 */
933#ifndef IN_RING3
934 if (!pRange->pfnOutStrCallback)
935 {
936# ifdef VBOX_WITH_STATISTICS
937 if (pStats)
938 STAM_COUNTER_INC(&pStats->CTXALLMID(Out,ToR3));
939# endif
940 return VINF_IOM_HC_IOPORT_WRITE;
941 }
942#endif
943 /* call the device. */
944#ifdef VBOX_WITH_STATISTICS
945 if (pStats)
946 STAM_PROFILE_ADV_START(&pStats->CTXALLSUFF(ProfOut), a);
947#endif
948 int rc = pRange->pfnOutStrCallback(pRange->pDevIns, pRange->pvUser, Port, pGCPtrSrc, pcTransfers, cb);
949#ifdef VBOX_WITH_STATISTICS
950 if (pStats)
951 STAM_PROFILE_ADV_STOP(&pStats->CTXALLSUFF(ProfOut), a);
952 if (rc == VINF_SUCCESS && pStats)
953 STAM_COUNTER_INC(&pStats->CTXALLSUFF(Out));
954# ifndef IN_RING3
955 else if (rc == VINF_IOM_HC_IOPORT_WRITE && pStats)
956 STAM_COUNTER_INC(&pStats->CTXALLMID(Out, ToR3));
957# endif
958#endif
959 Log3(("IOMIOPortWriteStr: Port=%RTiop pGCPtrSrc=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=%Vrc\n",
960 Port, pGCPtrSrc, pcTransfers, cTransfers, *pcTransfers, cb, rc));
961 return rc;
962 }
963
964#ifndef IN_RING3
965 /*
966 * Handler in ring-3?
967 */
968 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeHC(&pVM->iom.s, Port);
969 if (pRangeR3)
970 {
971# ifdef VBOX_WITH_STATISTICS
972 if (pStats)
973 STAM_COUNTER_INC(&pStats->CTXALLMID(Out,ToR3));
974# endif
975 return VINF_IOM_HC_IOPORT_WRITE;
976 }
977#endif
978
979 /*
980 * Ok, no handler for this port.
981 */
982#ifdef VBOX_WITH_STATISTICS
983 if (pStats)
984 STAM_COUNTER_INC(&pStats->CTXALLSUFF(Out));
985 else
986 {
987# ifndef IN_RING3
988 /* Ring-3 will have to create the statistics record. */
989 return VINF_IOM_HC_IOPORT_WRITE;
990# else
991 pStats = iomr3IOPortStatsCreate(pVM, Port, NULL);
992 if (pStats)
993 STAM_COUNTER_INC(&pStats->CTXALLSUFF(Out));
994# endif
995 }
996#endif
997
998 Log3(("IOMIOPortWriteStr: Port=%RTiop pGCPtrSrc=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=VINF_SUCCESS\n",
999 Port, pGCPtrSrc, pcTransfers, cTransfers, *pcTransfers, cb));
1000 return VINF_SUCCESS;
1001}
1002
1003
1004//#undef LOG_GROUP
1005//#define LOG_GROUP LOG_GROUP_IOM_MMIO
1006
1007/**
1008 * Reads a MMIO register.
1009 *
1010 * @returns VBox status code.
1011 *
1012 * @param pVM VM handle.
1013 * @param GCPhys The physical address to read.
1014 * @param pu32Value Where to store the value read.
1015 * @param cbValue The size of the register to read in bytes. 1, 2 or 4 bytes.
1016 */
1017IOMDECL(int) IOMMMIORead(PVM pVM, RTGCPHYS GCPhys, uint32_t *pu32Value, size_t cbValue)
1018{
1019/** @todo add return to ring-3 statistics when this function is used in GC! */
1020
1021 /*
1022 * Lookup the current context range node and statistics.
1023 */
1024 CTXALLSUFF(PIOMMMIORANGE) pRange = iomMMIOGetRange(&pVM->iom.s, GCPhys);
1025#ifdef VBOX_WITH_STATISTICS
1026 PIOMMMIOSTATS pStats = iomMMIOGetStats(&pVM->iom.s, GCPhys);
1027 if (!pStats && (!pRange || pRange->cbSize <= PAGE_SIZE))
1028# ifdef IN_RING3
1029 pStats = iomR3MMIOStatsCreate(pVM, GCPhys, pRange ? pRange->pszDesc : NULL);
1030# else
1031 return VINF_IOM_HC_MMIO_READ;
1032# endif
1033#endif /* VBOX_WITH_STATISTICS */
1034#ifdef IN_RING3
1035 if (pRange)
1036#else /* !IN_RING3 */
1037 if (pRange && pRange->pfnReadCallback)
1038#endif /* !IN_RING3 */
1039 {
1040 /*
1041 * Perform the read and deal with the result.
1042 */
1043#ifdef VBOX_WITH_STATISTICS
1044 if (pStats)
1045 STAM_PROFILE_ADV_START(&pStats->CTXALLSUFF(ProfRead), a);
1046#endif
1047 int rc = pRange->pfnReadCallback(pRange->pDevIns, pRange->pvUser, GCPhys, pu32Value, cbValue);
1048#ifdef VBOX_WITH_STATISTICS
1049 if (pStats)
1050 STAM_PROFILE_ADV_STOP(&pStats->CTXALLSUFF(ProfRead), a);
1051 if (pStats && rc != VINF_IOM_HC_MMIO_READ)
1052 STAM_COUNTER_INC(&pStats->CTXALLSUFF(Read));
1053#endif
1054 switch (rc)
1055 {
1056 case VINF_SUCCESS:
1057 default:
1058 Log4(("IOMMMIORead: GCPhys=%RGp *pu32=%08RX32 cb=%d rc=%Vrc\n", GCPhys, *pu32Value, cbValue, rc));
1059 return rc;
1060
1061 case VINF_IOM_MMIO_UNUSED_00:
1062 switch (cbValue)
1063 {
1064 case 1: *(uint8_t *)pu32Value = 0x00; break;
1065 case 2: *(uint16_t *)pu32Value = 0x0000; break;
1066 case 4: *(uint32_t *)pu32Value = 0x00000000; break;
1067 default: AssertReleaseMsgFailed(("cbValue=%d GCPhys=%VGp\n", cbValue, GCPhys)); break;
1068 }
1069 Log4(("IOMMMIORead: GCPhys=%RGp *pu32=%08RX32 cb=%d rc=%Vrc\n", GCPhys, *pu32Value, cbValue, rc));
1070 return VINF_SUCCESS;
1071
1072 case VINF_IOM_MMIO_UNUSED_FF:
1073 switch (cbValue)
1074 {
1075 case 1: *(uint8_t *)pu32Value = 0xff; break;
1076 case 2: *(uint16_t *)pu32Value = 0xffff; break;
1077 case 4: *(uint32_t *)pu32Value = 0xffffffff; break;
1078 default: AssertReleaseMsgFailed(("cbValue=%d GCPhys=%VGp\n", cbValue, GCPhys)); break;
1079 }
1080 Log4(("IOMMMIORead: GCPhys=%RGp *pu32=%08RX32 cb=%d rc=%Vrc\n", GCPhys, *pu32Value, cbValue, rc));
1081 return VINF_SUCCESS;
1082 }
1083 }
1084
1085#ifndef IN_RING3
1086 /*
1087 * Lookup the ring-3 range.
1088 */
1089 PIOMMMIORANGER3 pRangeR3 = iomMMIOGetRangeHC(&pVM->iom.s, GCPhys);
1090 if (pRangeR3)
1091 {
1092 if (pRangeR3->pfnReadCallback)
1093 return VINF_IOM_HC_MMIO_READ;
1094# ifdef VBOX_WITH_STATISTICS
1095 if (pStats)
1096 STAM_COUNTER_INC(&pStats->CTXALLSUFF(Read));
1097# endif
1098 *pu32Value = 0;
1099 Log4(("IOMMMIORead: GCPhys=%RGp *pu32=%08RX32 cb=%d rc=VINF_SUCCESS\n", GCPhys, *pu32Value, cbValue));
1100 return VINF_SUCCESS;
1101 }
1102#endif
1103
1104 AssertMsgFailed(("Handlers and page tables are out of sync or something! GCPhys=%VGp cbValue=%d\n", GCPhys, cbValue));
1105 return VERR_INTERNAL_ERROR;
1106}
1107
1108
1109/**
1110 * Writes to a MMIO register.
1111 *
1112 * @returns VBox status code.
1113 *
1114 * @param pVM VM handle.
1115 * @param GCPhys The physical address to write to.
1116 * @param u32Value The value to write.
1117 * @param cbValue The size of the register to read in bytes. 1, 2 or 4 bytes.
1118 */
1119IOMDECL(int) IOMMMIOWrite(PVM pVM, RTGCPHYS GCPhys, uint32_t u32Value, size_t cbValue)
1120{
1121/** @todo add return to ring-3 statistics when this function is used in GC! */
1122 /*
1123 * Lookup the current context range node.
1124 */
1125 CTXALLSUFF(PIOMMMIORANGE) pRange = iomMMIOGetRange(&pVM->iom.s, GCPhys);
1126#ifdef VBOX_WITH_STATISTICS
1127 PIOMMMIOSTATS pStats = iomMMIOGetStats(&pVM->iom.s, GCPhys);
1128 if (!pStats && (!pRange || pRange->cbSize <= PAGE_SIZE))
1129# ifdef IN_RING3
1130 pStats = iomR3MMIOStatsCreate(pVM, GCPhys, pRange ? pRange->pszDesc : NULL);
1131# else
1132 return VINF_IOM_HC_MMIO_WRITE;
1133# endif
1134#endif /* VBOX_WITH_STATISTICS */
1135
1136 /*
1137 * Perform the write if we found a range.
1138 */
1139#ifdef IN_RING3
1140 if (pRange)
1141#else /* !IN_RING3 */
1142 if (pRange && pRange->pfnWriteCallback)
1143#endif /* !IN_RING3 */
1144 {
1145#ifdef VBOX_WITH_STATISTICS
1146 if (pStats)
1147 STAM_PROFILE_ADV_START(&pStats->CTXALLSUFF(ProfWrite), a);
1148#endif
1149 int rc = pRange->pfnWriteCallback(pRange->pDevIns, pRange->pvUser, GCPhys, &u32Value, cbValue);
1150#ifdef VBOX_WITH_STATISTICS
1151 if (pStats)
1152 STAM_PROFILE_ADV_STOP(&pStats->CTXALLSUFF(ProfWrite), a);
1153 if (pStats && rc != VINF_IOM_HC_MMIO_WRITE)
1154 STAM_COUNTER_INC(&pStats->CTXALLSUFF(Write));
1155#endif
1156 Log4(("IOMMMIOWrite: GCPhys=%RGp u32=%08RX32 cb=%d rc=%Vrc\n", GCPhys, u32Value, cbValue, rc));
1157 return rc;
1158 }
1159
1160#ifndef IN_RING3
1161 /*
1162 * Lookup the ring-3 range.
1163 */
1164 PIOMMMIORANGER3 pRangeR3 = iomMMIOGetRangeHC(&pVM->iom.s, GCPhys);
1165 if (pRangeR3)
1166 {
1167 if (pRangeR3->pfnWriteCallback)
1168 return VINF_IOM_HC_MMIO_WRITE;
1169# ifdef VBOX_WITH_STATISTICS
1170 if (pStats)
1171 STAM_COUNTER_INC(&pStats->CTXALLSUFF(Write));
1172# endif
1173 Log4(("IOMMMIOWrite: GCPhys=%RGp u32=%08RX32 cb=%d rc=%Vrc\n", GCPhys, u32Value, cbValue));
1174 return VINF_SUCCESS;
1175 }
1176#endif
1177
1178 AssertMsgFailed(("Handlers and page tables are out of sync or something! GCPhys=%VGp cbValue=%d\n", GCPhys, cbValue));
1179 return VERR_INTERNAL_ERROR;
1180}
1181
1182
1183/**
1184 * Checks that the operation is allowed according to the IOPL
1185 * level and I/O bitmap.
1186 *
1187 * @returns VBox status code.
1188 * If not VINF_SUCCESS a \#GP(0) was raised or an error occured.
1189 *
1190 * @param pVM VM handle.
1191 * @param pCtxCore Pointer to register frame.
1192 * @param Port The I/O port number.
1193 * @param cb The access size.
1194 */
1195IOMDECL(int) IOMInterpretCheckPortIOAccess(PVM pVM, PCPUMCTXCORE pCtxCore, RTIOPORT Port, unsigned cb)
1196{
1197 /*
1198 * If this isn't ring-0, we have to check for I/O privileges.
1199 */
1200 uint32_t efl = CPUMRawGetEFlags(pVM, pCtxCore);
1201 if ( ( (unsigned)(pCtxCore->ss & X86_SEL_RPL) > 1
1202 && X86_EFL_GET_IOPL(efl) < (unsigned)(pCtxCore->ss & X86_SEL_RPL)
1203 )
1204 || (efl & X86_EFL_VM)
1205 )
1206 {
1207 /*
1208 * Get TSS location and check if there can be a I/O bitmap.
1209 */
1210 RTGCUINTPTR GCPtrTss;
1211 RTGCUINTPTR cbTss;
1212 bool fCanHaveIOBitmap;
1213 int rc = SELMGetTSSInfo(pVM, &GCPtrTss, &cbTss, &fCanHaveIOBitmap);
1214 if (VBOX_FAILURE(rc))
1215 {
1216 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d %Vrc -> #GP(0)\n", Port, cb, rc));
1217 return TRPMRaiseXcptErr(pVM, pCtxCore, X86_XCPT_GP, 0);
1218 }
1219
1220 if ( !fCanHaveIOBitmap
1221 || cbTss <= sizeof(VBOXTSS))
1222 {
1223 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d cbTss=%#x fCanHaveIOBitmap=%RTbool -> #GP(0)\n",
1224 Port, cb, cbTss, fCanHaveIOBitmap));
1225 return TRPMRaiseXcptErr(pVM, pCtxCore, X86_XCPT_GP, 0);
1226 }
1227
1228 /*
1229 * Fetch the I/O bitmap offset.
1230 */
1231 uint16_t offIOPB;
1232 rc = PGMPhysInterpretedRead(pVM, pCtxCore, &offIOPB, GCPtrTss + RT_OFFSETOF(VBOXTSS, offIoBitmap), sizeof(offIOPB));
1233 if (rc != VINF_SUCCESS)
1234 {
1235 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d GCPtrTss=%VGv %Vrc\n",
1236 Port, cb, GCPtrTss, rc));
1237 return rc;
1238 }
1239
1240 /*
1241 * Check the limit and read the two bitmap bytes.
1242 */
1243 uint32_t offTss = offIOPB + (Port >> 3);
1244 if (offTss + 1 >= cbTss)
1245 {
1246 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d offTss=%#x cbTss=%#x -> #GP(0)\n",
1247 Port, cb, offTss, cbTss));
1248 return TRPMRaiseXcptErr(pVM, pCtxCore, X86_XCPT_GP, 0);
1249 }
1250 uint16_t u16;
1251 rc = PGMPhysInterpretedRead(pVM, pCtxCore, &u16, GCPtrTss + offTss, sizeof(u16));
1252 if (rc != VINF_SUCCESS)
1253 {
1254 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d GCPtrTss=%VGv offTss=%#x -> %Vrc\n",
1255 Port, cb, GCPtrTss, offTss, rc));
1256 return rc;
1257 }
1258
1259 /*
1260 * All the bits must be clear.
1261 */
1262 if ((u16 >> (Port & 7)) & ((1 << cb) - 1))
1263 {
1264 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d u16=%#x -> #GP(0)\n",
1265 Port, cb, u16, offTss));
1266 return TRPMRaiseXcptErr(pVM, pCtxCore, X86_XCPT_GP, 0);
1267 }
1268 LogFlow(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d offTss=%#x cbTss=%#x u16=%#x -> OK\n",
1269 Port, cb, u16, offTss, cbTss));
1270 }
1271 return VINF_SUCCESS;
1272}
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