VirtualBox

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

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

string.h & stdio.h + header cleanups.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 42.2 KB
Line 
1/* $Id: IOMAll.cpp 23 2007-01-15 14:08:28Z 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 int rc = pRange->pfnInCallback(pRange->pDevIns, pRange->pvUser, Port, pu32Value, cbValue);
570#ifdef VBOX_WITH_STATISTICS
571 if (rc == VINF_SUCCESS && pStats)
572 STAM_COUNTER_INC(&pStats->CTXALLSUFF(In));
573# ifndef IN_RING3
574 else if (rc == VINF_IOM_HC_IOPORT_READ && pStats)
575 STAM_COUNTER_INC(&pStats->CTXALLMID(In,ToR3));
576# endif
577#endif
578 if (rc == VERR_IOM_IOPORT_UNUSED)
579 {
580 /* make return value */
581 rc = VINF_SUCCESS;
582 switch (cbValue)
583 {
584 case 1: *(uint8_t *)pu32Value = 0xff; break;
585 case 2: *(uint16_t *)pu32Value = 0xffff; break;
586 case 4: *(uint32_t *)pu32Value = 0xffffffff; break;
587 default:
588 AssertMsgFailed(("Invalid I/O port size %d. Port=%d\n", cbValue, Port));
589 return VERR_IOM_INVALID_IOPORT_SIZE;
590 }
591 }
592 Log3(("IOMIOPortRead: Port=%RTiop *pu32=%08RX32 cb=%d rc=%Vrc\n", Port, *pu32Value, cbValue, rc));
593 return rc;
594 }
595
596#ifndef IN_RING3
597 /*
598 * Handler in ring-3?
599 */
600 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeHC(&pVM->iom.s, Port);
601 if (pRangeR3)
602 {
603# ifdef VBOX_WITH_STATISTICS
604 if (pStats)
605 STAM_COUNTER_INC(&pStats->CTXALLMID(In,ToR3));
606# endif
607 return VINF_IOM_HC_IOPORT_READ;
608 }
609#endif
610
611 /*
612 * Ok, no handler for this port.
613 */
614#ifdef VBOX_WITH_STATISTICS
615 if (pStats)
616 STAM_COUNTER_INC(&pStats->CTXALLSUFF(In));
617 else
618 {
619# ifndef IN_RING3
620 /* Ring-3 will have to create the statistics record. */
621 return VINF_IOM_HC_IOPORT_READ;
622# else
623 pStats = iomr3IOPortStatsCreate(pVM, Port, NULL);
624 if (pStats)
625 STAM_COUNTER_INC(&pStats->CTXALLSUFF(In));
626# endif
627 }
628#endif
629
630 /* make return value */
631 switch (cbValue)
632 {
633 case 1: *(uint8_t *)pu32Value = 0xff; break;
634 case 2: *(uint16_t *)pu32Value = 0xffff; break;
635 case 4: *(uint32_t *)pu32Value = 0xffffffff; break;
636 default:
637 AssertMsgFailed(("Invalid I/O port size %d. Port=%d\n", cbValue, Port));
638 return VERR_IOM_INVALID_IOPORT_SIZE;
639 }
640 Log3(("IOMIOPortRead: Port=%RTiop *pu32=%08RX32 cb=%d rc=VINF_SUCCESS\n", Port, *pu32Value, cbValue));
641 return VINF_SUCCESS;
642}
643
644
645/**
646 * Reads the string buffer of an I/O port register.
647 *
648 * @returns VBox status code.
649 *
650 * @param pVM VM handle.
651 * @param Port The port to read.
652 * @param pGCPtrDst Pointer to the destination buffer (GC, incremented appropriately).
653 * @param pcTransfers Pointer to the number of transfer units to read, on return remaining transfer units.
654 * @param cb Size of the transfer unit (1, 2 or 4 bytes).
655 * */
656IOMDECL(int) IOMIOPortReadString(PVM pVM, RTIOPORT Port, PRTGCPTR pGCPtrDst, PRTGCUINTREG pcTransfers, unsigned cb)
657{
658#ifdef VBOX_WITH_STATISTICS
659 /*
660 * Get the statistics record.
661 */
662 RTGCUINTREG cTransfers = *pcTransfers;
663 PIOMIOPORTSTATS pStats = CTXALLSUFF(pVM->iom.s.pStatsLastRead);
664 if (!pStats || pStats->Core.Key != Port)
665 {
666 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTXSUFF(pTrees)->IOPortStatTree, Port);
667 if (pStats)
668 CTXALLSUFF(pVM->iom.s.pStatsLastRead) = pStats;
669 }
670#endif
671
672 /*
673 * Get handler for current context.
674 */
675 CTXALLSUFF(PIOMIOPORTRANGE) pRange = CTXALLSUFF(pVM->iom.s.pRangeLastRead);
676 if ( !pRange
677 || (unsigned)Port - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
678 {
679 pRange = iomIOPortGetRange(&pVM->iom.s, Port);
680 if (pRange)
681 CTXALLSUFF(pVM->iom.s.pRangeLastRead) = pRange;
682 }
683#ifdef IN_GC
684 Assert(!pRange || MMHyperIsInsideArea(pVM, pRange)); /** @todo r=bird: there is a macro for this which skips the #if'ing. */
685#endif
686
687 if (pRange)
688 {
689 /*
690 * Found a range.
691 */
692#ifndef IN_RING3
693 if (!pRange->pfnInStrCallback)
694 {
695# ifdef VBOX_WITH_STATISTICS
696 if (pStats)
697 STAM_COUNTER_INC(&pStats->CTXALLMID(In,ToR3));
698# endif
699 return VINF_IOM_HC_IOPORT_READ;
700 }
701#endif
702 /* call the device. */
703 int rc = pRange->pfnInStrCallback(pRange->pDevIns, pRange->pvUser, Port, pGCPtrDst, pcTransfers, cb);
704#ifdef VBOX_WITH_STATISTICS
705 if (rc == VINF_SUCCESS && pStats)
706 STAM_COUNTER_INC(&pStats->CTXALLSUFF(In));
707# ifndef IN_RING3
708 else if (rc == VINF_IOM_HC_IOPORT_READ && pStats)
709 STAM_COUNTER_INC(&pStats->CTXALLMID(In, ToR3));
710# endif
711#endif
712 Log3(("IOMIOPortReadStr: Port=%RTiop pGCPtrDst=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=%Vrc\n",
713 Port, pGCPtrDst, pcTransfers, cTransfers, *pcTransfers, cb, rc));
714 return rc;
715 }
716
717#ifndef IN_RING3
718 /*
719 * Handler in ring-3?
720 */
721 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeHC(&pVM->iom.s, Port);
722 if (pRangeR3)
723 {
724# ifdef VBOX_WITH_STATISTICS
725 if (pStats)
726 STAM_COUNTER_INC(&pStats->CTXALLMID(In,ToR3));
727# endif
728 return VINF_IOM_HC_IOPORT_READ;
729 }
730#endif
731
732 /*
733 * Ok, no handler for this port.
734 */
735#ifdef VBOX_WITH_STATISTICS
736 if (pStats)
737 STAM_COUNTER_INC(&pStats->CTXALLSUFF(In));
738 else
739 {
740# ifndef IN_RING3
741 /* Ring-3 will have to create the statistics record. */
742 return VINF_IOM_HC_IOPORT_READ;
743# else
744 pStats = iomr3IOPortStatsCreate(pVM, Port, NULL);
745 if (pStats)
746 STAM_COUNTER_INC(&pStats->CTXALLSUFF(In));
747# endif
748 }
749#endif
750
751 Log3(("IOMIOPortReadStr: Port=%RTiop pGCPtrDst=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=VINF_SUCCESS\n",
752 Port, pGCPtrDst, pcTransfers, cTransfers, *pcTransfers, cb));
753 return VINF_SUCCESS;
754}
755
756
757/**
758 * Writes to an I/O port register.
759 *
760 * @returns VBox status code.
761 *
762 * @param pVM VM handle.
763 * @param Port The port to write to.
764 * @param u32Value The value to write.
765 * @param cbValue The size of the register to read in bytes. 1, 2 or 4 bytes.
766 */
767IOMDECL(int) IOMIOPortWrite(PVM pVM, RTIOPORT Port, uint32_t u32Value, size_t cbValue)
768{
769/** @todo bird: When I get time, I'll remove the GC tree and link the GC entries to the ring-3 node. */
770#ifdef VBOX_WITH_STATISTICS
771 /*
772 * Find the statistics record.
773 */
774 PIOMIOPORTSTATS pStats = CTXALLSUFF(pVM->iom.s.pStatsLastWrite);
775 if (!pStats || pStats->Core.Key != Port)
776 {
777 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTXSUFF(pTrees)->IOPortStatTree, Port);
778 if (pStats)
779 CTXALLSUFF(pVM->iom.s.pStatsLastWrite) = pStats;
780 }
781#endif
782
783 /*
784 * Get handler for current context.
785 */
786 CTXALLSUFF(PIOMIOPORTRANGE) pRange = CTXALLSUFF(pVM->iom.s.pRangeLastWrite);
787 if ( !pRange
788 || (unsigned)Port - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
789 {
790 pRange = iomIOPortGetRange(&pVM->iom.s, Port);
791 if (pRange)
792 CTXALLSUFF(pVM->iom.s.pRangeLastWrite) = pRange;
793 }
794#ifdef IN_GC
795 Assert(!pRange || MMHyperIsInsideArea(pVM, pRange));
796#endif
797
798 if (pRange)
799 {
800 /*
801 * Found a range.
802 */
803#ifndef IN_RING3
804 if (!pRange->pfnOutCallback)
805 {
806# ifdef VBOX_WITH_STATISTICS
807 if (pStats)
808 STAM_COUNTER_INC(&pStats->CTXALLMID(Out,ToR3));
809# endif
810 return VINF_IOM_HC_IOPORT_WRITE;
811 }
812#endif
813 /* call the device. */
814 int rc = pRange->pfnOutCallback(pRange->pDevIns, pRange->pvUser, Port, u32Value, cbValue);
815
816#ifdef VBOX_WITH_STATISTICS
817 if (rc == VINF_SUCCESS && pStats)
818 STAM_COUNTER_INC(&pStats->CTXALLSUFF(Out));
819# ifndef IN_RING3
820 else if (rc == VINF_IOM_HC_IOPORT_WRITE && pStats)
821 STAM_COUNTER_INC(&pStats->CTXALLMID(Out, ToR3));
822# endif
823#endif
824 Log3(("IOMIOPortWrite: Port=%RTiop u32=%08RX32 cb=%d rc=%Vrc\n", Port, u32Value, cbValue, rc));
825 return rc;
826 }
827
828#ifndef IN_RING3
829 /*
830 * Handler in ring-3?
831 */
832 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeHC(&pVM->iom.s, Port);
833 if (pRangeR3)
834 {
835# ifdef VBOX_WITH_STATISTICS
836 if (pStats)
837 STAM_COUNTER_INC(&pStats->CTXALLMID(Out,ToR3));
838# endif
839 return VINF_IOM_HC_IOPORT_WRITE;
840 }
841#endif
842
843 /*
844 * Ok, no handler for that port.
845 */
846#ifdef VBOX_WITH_STATISTICS
847 /* statistics. */
848 if (pStats)
849 STAM_COUNTER_INC(&pStats->CTXALLSUFF(Out));
850 else
851 {
852# ifndef IN_RING3
853 /* R3 will have to create the statistics record. */
854 return VINF_IOM_HC_IOPORT_WRITE;
855# else
856 pStats = iomr3IOPortStatsCreate(pVM, Port, NULL);
857 if (pStats)
858 STAM_COUNTER_INC(&pStats->CTXALLSUFF(Out));
859# endif
860 }
861#endif
862 Log3(("IOMIOPortWrite: Port=%RTiop u32=%08RX32 cb=%d nop\n", Port, u32Value, cbValue));
863 return VINF_SUCCESS;
864}
865
866
867/**
868 * Writes the string buffer of an I/O port register.
869 *
870 * @returns VBox status code.
871 *
872 * @param pVM VM handle.
873 * @param Port The port to write.
874 * @param pGCPtrSrc Pointer to the source buffer (GC, incremented appropriately).
875 * @param pcTransfers Pointer to the number of transfer units to write, on return remaining transfer units.
876 * @param cb Size of the transfer unit (1, 2 or 4 bytes).
877 * */
878IOMDECL(int) IOMIOPortWriteString(PVM pVM, RTIOPORT Port, PRTGCPTR pGCPtrSrc, PRTGCUINTREG pcTransfers, unsigned cb)
879{
880#ifdef VBOX_WITH_STATISTICS
881 /*
882 * Get the statistics record.
883 */
884 const RTGCUINTREG cTransfers = *pcTransfers;
885 PIOMIOPORTSTATS pStats = CTXALLSUFF(pVM->iom.s.pStatsLastWrite);
886 if (!pStats || pStats->Core.Key != Port)
887 {
888 pStats = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.CTXSUFF(pTrees)->IOPortStatTree, Port);
889 if (pStats)
890 CTXALLSUFF(pVM->iom.s.pStatsLastWrite) = pStats;
891 }
892#endif
893
894 /*
895 * Get handler for current context.
896 */
897 CTXALLSUFF(PIOMIOPORTRANGE) pRange = CTXALLSUFF(pVM->iom.s.pRangeLastWrite);
898 if ( !pRange
899 || (unsigned)Port - (unsigned)pRange->Port >= (unsigned)pRange->cPorts)
900 {
901 pRange = iomIOPortGetRange(&pVM->iom.s, Port);
902 if (pRange)
903 CTXALLSUFF(pVM->iom.s.pRangeLastWrite) = pRange;
904 }
905#ifdef IN_GC
906 Assert(!pRange || MMHyperIsInsideArea(pVM, pRange)); /** @todo r=bird: there is a macro for this which skips the #if'ing. */
907#endif
908
909 if (pRange)
910 {
911 /*
912 * Found a range.
913 */
914#ifndef IN_RING3
915 if (!pRange->pfnOutStrCallback)
916 {
917# ifdef VBOX_WITH_STATISTICS
918 if (pStats)
919 STAM_COUNTER_INC(&pStats->CTXALLMID(Out,ToR3));
920# endif
921 return VINF_IOM_HC_IOPORT_WRITE;
922 }
923#endif
924 /* call the device. */
925 int rc = pRange->pfnOutStrCallback(pRange->pDevIns, pRange->pvUser, Port, pGCPtrSrc, pcTransfers, cb);
926#ifdef VBOX_WITH_STATISTICS
927 if (rc == VINF_SUCCESS && pStats)
928 STAM_COUNTER_INC(&pStats->CTXALLSUFF(Out));
929# ifndef IN_RING3
930 else if (rc == VINF_IOM_HC_IOPORT_WRITE && pStats)
931 STAM_COUNTER_INC(&pStats->CTXALLMID(Out, ToR3));
932# endif
933#endif
934 Log3(("IOMIOPortWriteStr: Port=%RTiop pGCPtrSrc=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=%Vrc\n",
935 Port, pGCPtrSrc, pcTransfers, cTransfers, *pcTransfers, cb, rc));
936 return rc;
937 }
938
939#ifndef IN_RING3
940 /*
941 * Handler in ring-3?
942 */
943 PIOMIOPORTRANGER3 pRangeR3 = iomIOPortGetRangeHC(&pVM->iom.s, Port);
944 if (pRangeR3)
945 {
946# ifdef VBOX_WITH_STATISTICS
947 if (pStats)
948 STAM_COUNTER_INC(&pStats->CTXALLMID(Out,ToR3));
949# endif
950 return VINF_IOM_HC_IOPORT_WRITE;
951 }
952#endif
953
954 /*
955 * Ok, no handler for this port.
956 */
957#ifdef VBOX_WITH_STATISTICS
958 if (pStats)
959 STAM_COUNTER_INC(&pStats->CTXALLSUFF(Out));
960 else
961 {
962# ifndef IN_RING3
963 /* Ring-3 will have to create the statistics record. */
964 return VINF_IOM_HC_IOPORT_WRITE;
965# else
966 pStats = iomr3IOPortStatsCreate(pVM, Port, NULL);
967 if (pStats)
968 STAM_COUNTER_INC(&pStats->CTXALLSUFF(Out));
969# endif
970 }
971#endif
972
973 Log3(("IOMIOPortWriteStr: Port=%RTiop pGCPtrSrc=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=VINF_SUCCESS\n",
974 Port, pGCPtrSrc, pcTransfers, cTransfers, *pcTransfers, cb));
975 return VINF_SUCCESS;
976}
977
978
979//#undef LOG_GROUP
980//#define LOG_GROUP LOG_GROUP_IOM_MMIO
981
982/**
983 * Reads a MMIO register.
984 *
985 * @returns VBox status code.
986 *
987 * @param pVM VM handle.
988 * @param GCPhys The physical address to read.
989 * @param pu32Value Where to store the value read.
990 * @param cbValue The size of the register to read in bytes. 1, 2 or 4 bytes.
991 */
992IOMDECL(int) IOMMMIORead(PVM pVM, RTGCPHYS GCPhys, uint32_t *pu32Value, size_t cbValue)
993{
994/** @todo add return to ring-3 statistics when this function is used in GC! */
995
996 /*
997 * Lookup the current context range node and statistics.
998 */
999 CTXALLSUFF(PIOMMMIORANGE) pRange = iomMMIOGetRange(&pVM->iom.s, GCPhys);
1000#ifdef VBOX_WITH_STATISTICS
1001 PIOMMMIOSTATS pStats = iomMMIOGetStats(&pVM->iom.s, GCPhys);
1002 if (!pStats && (!pRange || pRange->cbSize <= PAGE_SIZE))
1003# ifdef IN_RING3
1004 pStats = iomR3MMIOStatsCreate(pVM, GCPhys, pRange ? pRange->pszDesc : NULL);
1005# else
1006 return VINF_IOM_HC_MMIO_READ;
1007# endif
1008#endif /* VBOX_WITH_STATISTICS */
1009#ifdef IN_RING3
1010 if (pRange)
1011#else /* !IN_RING3 */
1012 if (pRange && pRange->pfnReadCallback)
1013#endif /* !IN_RING3 */
1014 {
1015 /*
1016 * Perform the read and deal with the result.
1017 */
1018 int rc = pRange->pfnReadCallback(pRange->pDevIns, pRange->pvUser, GCPhys, pu32Value, cbValue);
1019#ifdef VBOX_WITH_STATISTICS
1020 if (pStats && rc != VINF_IOM_HC_MMIO_READ)
1021 STAM_COUNTER_INC(&pStats->CTXALLSUFF(Read));
1022#endif
1023 switch (rc)
1024 {
1025 case VINF_SUCCESS:
1026 default:
1027 Log4(("IOMMMIORead: GCPhys=%RGp *pu32=%08RX32 cb=%d rc=%Vrc\n", GCPhys, *pu32Value, cbValue, rc));
1028 return rc;
1029
1030 case VINF_IOM_MMIO_UNUSED_00:
1031 switch (cbValue)
1032 {
1033 case 1: *(uint8_t *)pu32Value = 0x00; break;
1034 case 2: *(uint16_t *)pu32Value = 0x0000; break;
1035 case 4: *(uint32_t *)pu32Value = 0x00000000; break;
1036 default: AssertReleaseMsgFailed(("cbValue=%d GCPhys=%VGp\n", cbValue, GCPhys)); break;
1037 }
1038 Log4(("IOMMMIORead: GCPhys=%RGp *pu32=%08RX32 cb=%d rc=%Vrc\n", GCPhys, *pu32Value, cbValue, rc));
1039 return VINF_SUCCESS;
1040
1041 case VINF_IOM_MMIO_UNUSED_FF:
1042 switch (cbValue)
1043 {
1044 case 1: *(uint8_t *)pu32Value = 0xff; break;
1045 case 2: *(uint16_t *)pu32Value = 0xffff; break;
1046 case 4: *(uint32_t *)pu32Value = 0xffffffff; break;
1047 default: AssertReleaseMsgFailed(("cbValue=%d GCPhys=%VGp\n", cbValue, GCPhys)); break;
1048 }
1049 Log4(("IOMMMIORead: GCPhys=%RGp *pu32=%08RX32 cb=%d rc=%Vrc\n", GCPhys, *pu32Value, cbValue, rc));
1050 return VINF_SUCCESS;
1051 }
1052 }
1053
1054#ifndef IN_RING3
1055 /*
1056 * Lookup the ring-3 range.
1057 */
1058 PIOMMMIORANGER3 pRangeR3 = iomMMIOGetRangeHC(&pVM->iom.s, GCPhys);
1059 if (pRangeR3)
1060 {
1061 if (pRangeR3->pfnReadCallback)
1062 return VINF_IOM_HC_MMIO_READ;
1063# ifdef VBOX_WITH_STATISTICS
1064 if (pStats)
1065 STAM_COUNTER_INC(&pStats->CTXALLSUFF(Read));
1066# endif
1067 *pu32Value = 0;
1068 Log4(("IOMMMIORead: GCPhys=%RGp *pu32=%08RX32 cb=%d rc=VINF_SUCCESS\n", GCPhys, *pu32Value, cbValue));
1069 return VINF_SUCCESS;
1070 }
1071#endif
1072
1073 AssertMsgFailed(("Handlers and page tables are out of sync or something! GCPhys=%VGp cbValue=%d\n", GCPhys, cbValue));
1074 return VERR_INTERNAL_ERROR;
1075}
1076
1077
1078/**
1079 * Writes to a MMIO register.
1080 *
1081 * @returns VBox status code.
1082 *
1083 * @param pVM VM handle.
1084 * @param GCPhys The physical address to write to.
1085 * @param u32Value The value to write.
1086 * @param cbValue The size of the register to read in bytes. 1, 2 or 4 bytes.
1087 */
1088IOMDECL(int) IOMMMIOWrite(PVM pVM, RTGCPHYS GCPhys, uint32_t u32Value, size_t cbValue)
1089{
1090/** @todo add return to ring-3 statistics when this function is used in GC! */
1091 /*
1092 * Lookup the current context range node.
1093 */
1094 CTXALLSUFF(PIOMMMIORANGE) pRange = iomMMIOGetRange(&pVM->iom.s, GCPhys);
1095#ifdef VBOX_WITH_STATISTICS
1096 PIOMMMIOSTATS pStats = iomMMIOGetStats(&pVM->iom.s, GCPhys);
1097 if (!pStats && (!pRange || pRange->cbSize <= PAGE_SIZE))
1098# ifdef IN_RING3
1099 pStats = iomR3MMIOStatsCreate(pVM, GCPhys, pRange ? pRange->pszDesc : NULL);
1100# else
1101 return VINF_IOM_HC_MMIO_WRITE;
1102# endif
1103#endif /* VBOX_WITH_STATISTICS */
1104
1105 /*
1106 * Perform the write if we found a range.
1107 */
1108#ifdef IN_RING3
1109 if (pRange)
1110#else /* !IN_RING3 */
1111 if (pRange && pRange->pfnWriteCallback)
1112#endif /* !IN_RING3 */
1113 {
1114 int rc = pRange->pfnWriteCallback(pRange->pDevIns, pRange->pvUser, GCPhys, &u32Value, cbValue);
1115#ifdef VBOX_WITH_STATISTICS
1116 if (pStats && rc != VINF_IOM_HC_MMIO_WRITE)
1117 STAM_COUNTER_INC(&pStats->CTXALLSUFF(Write));
1118#endif
1119 Log4(("IOMMMIOWrite: GCPhys=%RGp u32=%08RX32 cb=%d rc=%Vrc\n", GCPhys, u32Value, cbValue, rc));
1120 return rc;
1121 }
1122
1123#ifndef IN_RING3
1124 /*
1125 * Lookup the ring-3 range.
1126 */
1127 PIOMMMIORANGER3 pRangeR3 = iomMMIOGetRangeHC(&pVM->iom.s, GCPhys);
1128 if (pRangeR3)
1129 {
1130 if (pRangeR3->pfnWriteCallback)
1131 return VINF_IOM_HC_MMIO_WRITE;
1132# ifdef VBOX_WITH_STATISTICS
1133 if (pStats)
1134 STAM_COUNTER_INC(&pStats->CTXALLSUFF(Write));
1135# endif
1136 Log4(("IOMMMIOWrite: GCPhys=%RGp u32=%08RX32 cb=%d rc=%Vrc\n", GCPhys, u32Value, cbValue));
1137 return VINF_SUCCESS;
1138 }
1139#endif
1140
1141 AssertMsgFailed(("Handlers and page tables are out of sync or something! GCPhys=%VGp cbValue=%d\n", GCPhys, cbValue));
1142 return VERR_INTERNAL_ERROR;
1143}
1144
1145
1146/**
1147 * Checks that the operation is allowed according to the IOPL
1148 * level and I/O bitmap.
1149 *
1150 * @returns VBox status code.
1151 * If not VINF_SUCCESS a \#GP(0) was raised or an error occured.
1152 *
1153 * @param pVM VM handle.
1154 * @param pCtxCore Pointer to register frame.
1155 * @param Port The I/O port number.
1156 * @param cb The access size.
1157 */
1158IOMDECL(int) IOMInterpretCheckPortIOAccess(PVM pVM, PCPUMCTXCORE pCtxCore, RTIOPORT Port, unsigned cb)
1159{
1160 /*
1161 * If this isn't ring-0, we have to check for I/O privileges.
1162 */
1163 uint32_t efl = CPUMRawGetEFlags(pVM, pCtxCore);
1164 if ( ( (unsigned)(pCtxCore->ss & X86_SEL_RPL) > 1
1165 && X86_EFL_GET_IOPL(efl) < (unsigned)(pCtxCore->ss & X86_SEL_RPL)
1166 )
1167 || (efl & X86_EFL_VM)
1168 )
1169 {
1170 /*
1171 * Get TSS location and check if there can be a I/O bitmap.
1172 */
1173 RTGCUINTPTR GCPtrTss;
1174 RTGCUINTPTR cbTss;
1175 bool fCanHaveIOBitmap;
1176 int rc = SELMGetTSSInfo(pVM, &GCPtrTss, &cbTss, &fCanHaveIOBitmap);
1177 if (VBOX_FAILURE(rc))
1178 {
1179 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d %Vrc -> #GP(0)\n", Port, cb, rc));
1180 return TRPMRaiseXcptErr(pVM, pCtxCore, X86_XCPT_GP, 0);
1181 }
1182
1183 if ( !fCanHaveIOBitmap
1184 || cbTss <= sizeof(VBOXTSS))
1185 {
1186 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d cbTss=%#x fCanHaveIOBitmap=%RTbool -> #GP(0)\n",
1187 Port, cb, cbTss, fCanHaveIOBitmap));
1188 return TRPMRaiseXcptErr(pVM, pCtxCore, X86_XCPT_GP, 0);
1189 }
1190
1191 /*
1192 * Fetch the I/O bitmap offset.
1193 */
1194 uint16_t offIOPB;
1195 rc = PGMPhysInterpretedRead(pVM, pCtxCore, &offIOPB, GCPtrTss + RT_OFFSETOF(VBOXTSS, offIoBitmap), sizeof(offIOPB));
1196 if (rc != VINF_SUCCESS)
1197 {
1198 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d GCPtrTss=%VGv %Vrc\n",
1199 Port, cb, GCPtrTss, rc));
1200 return rc;
1201 }
1202
1203 /*
1204 * Check the limit and read the two bitmap bytes.
1205 */
1206 uint32_t offTss = offIOPB + (Port >> 3);
1207 if (offTss + 1 >= cbTss)
1208 {
1209 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d offTss=%#x cbTss=%#x -> #GP(0)\n",
1210 Port, cb, offTss, cbTss));
1211 return TRPMRaiseXcptErr(pVM, pCtxCore, X86_XCPT_GP, 0);
1212 }
1213 uint16_t u16;
1214 rc = PGMPhysInterpretedRead(pVM, pCtxCore, &u16, GCPtrTss + offTss, sizeof(u16));
1215 if (rc != VINF_SUCCESS)
1216 {
1217 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d GCPtrTss=%VGv offTss=%#x -> %Vrc\n",
1218 Port, cb, GCPtrTss, offTss, rc));
1219 return rc;
1220 }
1221
1222 /*
1223 * All the bits must be clear.
1224 */
1225 if ((u16 >> (Port & 7)) & ((1 << cb) - 1))
1226 {
1227 Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d u16=%#x -> #GP(0)\n",
1228 Port, cb, u16, offTss));
1229 return TRPMRaiseXcptErr(pVM, pCtxCore, X86_XCPT_GP, 0);
1230 }
1231 LogFlow(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d offTss=%#x cbTss=%#x u16=%#x -> OK\n",
1232 Port, cb, u16, offTss, cbTss));
1233 }
1234 return VINF_SUCCESS;
1235}
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