VirtualBox

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

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

Cleaned up cpl checking.

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