VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/DevIoApic.cpp@ 44690

Last change on this file since 44690 was 44690, checked in by vboxsync, 12 years ago

DevIoApic.cpp: Build fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 33.8 KB
Line 
1/* $Id: DevIoApic.cpp 44690 2013-02-14 13:24:07Z vboxsync $ */
2/** @file
3 * I/O Advanced Programmable Interrupt Controller (IO-APIC) Device.
4 */
5
6/*
7 * Copyright (C) 2006-2013 Oracle Corporation
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 (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 * --------------------------------------------------------------------
17 *
18 * This code is based on:
19 *
20 * apic.c revision 1.5 @@OSETODO
21 *
22 * APIC support
23 *
24 * Copyright (c) 2004-2005 Fabrice Bellard
25 *
26 * This library is free software; you can redistribute it and/or
27 * modify it under the terms of the GNU Lesser General Public
28 * License as published by the Free Software Foundation; either
29 * version 2 of the License, or (at your option) any later version.
30 *
31 * This library is distributed in the hope that it will be useful,
32 * but WITHOUT ANY WARRANTY; without even the implied warranty of
33 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
34 * Lesser General Public License for more details.
35 *
36 * You should have received a copy of the GNU Lesser General Public
37 * License along with this library; if not, write to the Free Software
38 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
39 */
40
41/*******************************************************************************
42* Header Files *
43*******************************************************************************/
44#define LOG_GROUP LOG_GROUP_DEV_APIC
45#include <VBox/vmm/pdmdev.h>
46
47#include <VBox/log.h>
48#include <VBox/vmm/stam.h>
49#include <iprt/assert.h>
50#include <iprt/asm.h>
51
52#include <VBox/msi.h>
53
54#include "VBoxDD2.h"
55#include "DevApic.h"
56
57
58/*******************************************************************************
59* Defined Constants And Macros *
60*******************************************************************************/
61/** @def IOAPIC_LOCK
62 * Acquires the PDM lock. */
63#define IOAPIC_LOCK(pThis, rc) \
64 do { \
65 int rc2 = (pThis)->CTX_SUFF(pIoApicHlp)->pfnLock((pThis)->CTX_SUFF(pDevIns), rc); \
66 if (rc2 != VINF_SUCCESS) \
67 return rc2; \
68 } while (0)
69
70/** @def IOAPIC_UNLOCK
71 * Releases the PDM lock. */
72#define IOAPIC_UNLOCK(pThis) (pThis)->CTX_SUFF(pIoApicHlp)->pfnUnlock((pThis)->CTX_SUFF(pDevIns))
73
74#define DEBUG_IOAPIC
75#define IOAPIC_NUM_PINS 0x18
76
77
78/*******************************************************************************
79* Structures and Typedefs *
80*******************************************************************************/
81typedef struct IOAPIC
82{
83 uint8_t id;
84 uint8_t ioregsel;
85 uint8_t cCpus;
86
87 uint32_t irr;
88 uint64_t ioredtbl[IOAPIC_NUM_PINS];
89 /** The IRQ tags and source IDs for each pin (tracing purposes). */
90 uint32_t auTagSrc[IOAPIC_NUM_PINS];
91
92 /** The device instance - R3 Ptr. */
93 PPDMDEVINSR3 pDevInsR3;
94 /** The IOAPIC helpers - R3 Ptr. */
95 PCPDMIOAPICHLPR3 pIoApicHlpR3;
96
97 /** The device instance - R0 Ptr. */
98 PPDMDEVINSR0 pDevInsR0;
99 /** The IOAPIC helpers - R0 Ptr. */
100 PCPDMIOAPICHLPR0 pIoApicHlpR0;
101
102 /** The device instance - RC Ptr. */
103 PPDMDEVINSRC pDevInsRC;
104 /** The IOAPIC helpers - RC Ptr. */
105 PCPDMIOAPICHLPRC pIoApicHlpRC;
106
107# ifdef VBOX_WITH_STATISTICS
108 STAMCOUNTER StatMMIOReadGC;
109 STAMCOUNTER StatMMIOReadHC;
110 STAMCOUNTER StatMMIOWriteGC;
111 STAMCOUNTER StatMMIOWriteHC;
112 STAMCOUNTER StatSetIrqGC;
113 STAMCOUNTER StatSetIrqHC;
114# endif
115} IOAPIC;
116typedef IOAPIC *PIOAPIC;
117
118#ifndef VBOX_DEVICE_STRUCT_TESTCASE
119
120/*******************************************************************************
121* Internal Functions *
122*******************************************************************************/
123
124
125static void ioapic_service(PIOAPIC pThis)
126{
127 uint8_t i;
128 uint8_t trig_mode;
129 uint8_t vector;
130 uint8_t delivery_mode;
131 uint32_t mask;
132 uint64_t entry;
133 uint8_t dest;
134 uint8_t dest_mode;
135 uint8_t polarity;
136
137 for (i = 0; i < IOAPIC_NUM_PINS; i++)
138 {
139 mask = 1 << i;
140 if (pThis->irr & mask)
141 {
142 entry = pThis->ioredtbl[i];
143 if (!(entry & APIC_LVT_MASKED))
144 {
145 trig_mode = ((entry >> 15) & 1);
146 dest = entry >> 56;
147 dest_mode = (entry >> 11) & 1;
148 delivery_mode = (entry >> 8) & 7;
149 polarity = (entry >> 13) & 1;
150 uint32_t uTagSrc = pThis->auTagSrc[i];
151 if (trig_mode == APIC_TRIGGER_EDGE)
152 {
153 pThis->auTagSrc[i] = 0;
154 pThis->irr &= ~mask;
155 }
156 if (delivery_mode == APIC_DM_EXTINT)
157 /* malc: i'm still not so sure about ExtINT delivery */
158 {
159 AssertMsgFailed(("Delivery mode ExtINT"));
160 vector = 0xff; /* incorrect but shuts up gcc. */
161 }
162 else
163 vector = entry & 0xff;
164
165 int rc = pThis->CTX_SUFF(pIoApicHlp)->pfnApicBusDeliver(pThis->CTX_SUFF(pDevIns),
166 dest,
167 dest_mode,
168 delivery_mode,
169 vector,
170 polarity,
171 trig_mode,
172 uTagSrc);
173 /* We must be sure that attempts to reschedule in R3
174 never get here */
175 Assert(rc == VINF_SUCCESS);
176 }
177 }
178 }
179}
180
181
182static void ioapic_set_irq(PIOAPIC pThis, int vector, int level, uint32_t uTagSrc)
183{
184 if (vector >= 0 && vector < IOAPIC_NUM_PINS)
185 {
186 uint32_t mask = 1 << vector;
187 uint64_t entry = pThis->ioredtbl[vector];
188
189 if ((entry >> 15) & 1)
190 {
191 /* level triggered */
192 if (level)
193 {
194 pThis->irr |= mask;
195 if (!pThis->auTagSrc[vector])
196 pThis->auTagSrc[vector] = uTagSrc;
197 else
198 pThis->auTagSrc[vector] = RT_BIT_32(31);
199
200 ioapic_service(pThis);
201
202 if ((level & PDM_IRQ_LEVEL_FLIP_FLOP) == PDM_IRQ_LEVEL_FLIP_FLOP)
203 {
204 pThis->irr &= ~mask;
205 pThis->auTagSrc[vector] = 0;
206 }
207 }
208 else
209 {
210 pThis->irr &= ~mask;
211 pThis->auTagSrc[vector] = 0;
212 }
213 }
214 else
215 {
216 /* edge triggered */
217 if (level)
218 {
219 pThis->irr |= mask;
220 if (!pThis->auTagSrc[vector])
221 pThis->auTagSrc[vector] = uTagSrc;
222 else
223 pThis->auTagSrc[vector] = RT_BIT_32(31);
224
225 ioapic_service(pThis);
226 }
227 }
228 }
229}
230
231
232/**
233 * Handles a read from the IOAPICID register.
234 */
235static int ioapic_IoApicId_r(PIOAPIC pThis, uint32_t *pu32Value)
236{
237 *pu32Value = (uint32_t)pThis->id << 24;
238 return VINF_SUCCESS;
239}
240
241
242/**
243 * Handles a write to the IOAPICID register.
244 */
245static int ioapic_IoApicId_w(PIOAPIC pThis, uint32_t u32Value)
246{
247 /* Note! Compared to the 82093AA spec, we've extended the IOAPIC
248 identification from bits 27:24 to bits 31:24. */
249 Log(("ioapic: IOAPICID %#x -> %#x\n", pThis->id, u32Value >> 24));
250 pThis->id = u32Value >> 24;
251 return VINF_SUCCESS;
252}
253
254
255/**
256 * Handles a read from the IOAPICVER register.
257 */
258static int ioapic_IoApicVer_r(PIOAPIC pThis, uint32_t *pu32Value)
259{
260 *pu32Value = RT_MAKE_U32(0x11, IOAPIC_NUM_PINS - 1); /* (0x11 is the version.) */
261 return VINF_SUCCESS;
262}
263
264
265/**
266 * Handles a read from the IOAPICARB register.
267 */
268static int ioapic_IoApicArb_r(PIOAPIC pThis, uint32_t *pu32Value)
269{
270 *pu32Value = 0; /* (arbitration winner) */
271 return VINF_SUCCESS;
272}
273
274
275/**
276 * Handles a read from the IOREGSEL register.
277 */
278static int ioapic_IoRegSel_r(PIOAPIC pThis, uint32_t *pu32Value)
279{
280 *pu32Value = pThis->ioregsel;
281 return VINF_SUCCESS;
282}
283
284/**
285 * Handles a write to the IOREGSEL register.
286 */
287static int ioapic_IoRegSel_w(PIOAPIC pThis, uint32_t u32Value)
288{
289 Log2(("ioapic: IOREGSEL %#04x -> %#04x\n", pThis->ioregsel, u32Value & 0xff));
290 /* Bits 7:0 are writable, the rest aren't. Confirmed on recent AMD box. */
291 pThis->ioregsel = u32Value & 0xff;
292 return VINF_SUCCESS;
293}
294
295
296/**
297 * Handles a write to the IOWIN register.
298 */
299static int ioapic_IoWin_r(PIOAPIC pThis, uint32_t *pu32Value)
300{
301 int rc = VINF_SUCCESS;
302 uint32_t const uIoRegSel = pThis->ioregsel;
303
304 if (uIoRegSel == 0)
305 rc = ioapic_IoApicId_r(pThis, pu32Value);
306 else if (uIoRegSel == 1)
307 rc = ioapic_IoApicVer_r(pThis, pu32Value);
308 else if (uIoRegSel == 2)
309 rc = ioapic_IoApicArb_r(pThis, pu32Value);
310 /*
311 * IOREDTBL0..IOREDTBL23.
312 */
313 else if (uIoRegSel - UINT32_C(0x10) < IOAPIC_NUM_PINS * 2)
314 {
315 uint32_t const idxIoRedTbl = (uIoRegSel - UINT32_C(0x10)) >> 1;
316 if (!(uIoRegSel & 1))
317 /** @todo r=bird: Do we need to emulate DELIVS or/and Remote IRR? */
318 *pu32Value = RT_LODWORD(pThis->ioredtbl[idxIoRedTbl]);
319 else
320 *pu32Value = RT_HIDWORD(pThis->ioredtbl[idxIoRedTbl]);
321 }
322 else
323 {
324 Log(("ioapic: Attempt to read from register %#x.\n", uIoRegSel));
325 *pu32Value = UINT32_MAX;
326 }
327
328 Log(("ioapic: IOWIN rd -> %#010x (%Rrc)\n", *pu32Value, rc));
329 return rc;
330}
331
332
333/**
334 * Handles a write to the IOWIN register.
335 */
336static int ioapic_IoWin_w(PIOAPIC pThis, uint32_t u32Value)
337{
338 int rc = VINF_SUCCESS;
339 uint32_t const uIoRegSel = pThis->ioregsel;
340 Log2(("ioapic: IOWIN[%#04x] = %#x\n", uIoRegSel, u32Value));
341
342 /*
343 * IOAPICID.
344 */
345 if (uIoRegSel == 0)
346 rc = ioapic_IoApicId_w(pThis, u32Value);
347 /*
348 * IOREDTBL0..IOREDTBL23.
349 */
350 else if (uIoRegSel - UINT32_C(0x10) < IOAPIC_NUM_PINS * 2)
351 {
352 uint32_t const idxIoRedTbl = (uIoRegSel - UINT32_C(0x10)) >> 1;
353 uint64_t u64NewValue;
354 if (!(uIoRegSel & 1))
355 {
356 /*
357 * Low DWORD.
358 *
359 * Have to do some sanity checks here because Linux 2.6 kernels
360 * writes seemingly bogus value (u32Value = 0) in their
361 * unlock_ExtINT_logic() function. Not sure what it's good for, but
362 * we ran into trouble with INTVEC = 0. Luckily the 82093AA specs
363 * limits the INTVEC range to 0x10 thru 0xfe, so we use this to
364 * ignore harmful values.
365 *
366 * Update: Looking at real hw (recent AMD), they don't reject
367 * invalid vector numbers, at least not at this point. Could be that
368 * some other code path needs to refuse something instead. Results:
369 * - Writing 0 to lo+hi -> 0.
370 * - Writing ~0 to lo+hi -> 0xff0000000001afff.
371 * - Writing ~0 w/ DELMOD set to 011b or 110b (both reserved)
372 * results in DELMOD containing the reserved values.
373 * - Ditto with same + DELMOD in [0..7], DELMOD is stored as written.
374 */
375 if ( (u32Value & APIC_LVT_MASKED)
376 || ((u32Value & UINT32_C(0xff)) - UINT32_C(0x10)) <= UINT32_C(0xee) /* (0xfe - 0x10 = 0xee) */ )
377 u64NewValue = (pThis->ioredtbl[idxIoRedTbl] & (UINT64_C(0xffffffff00000000) | RT_BIT(14) | RT_BIT(12)))
378 | (u32Value & ~(RT_BIT(14) | RT_BIT(12)));
379 else
380 {
381 LogRel(("IOAPIC GUEST BUG: bad vector writing %x(sel=%x) to %u\n", u32Value, uIoRegSel, idxIoRedTbl));
382 u64NewValue = pThis->ioredtbl[idxIoRedTbl];
383 }
384 }
385 else
386 {
387 /*
388 * High DWORD.
389 */
390 u64NewValue = (pThis->ioredtbl[idxIoRedTbl] & UINT64_C(0x00000000ffffffff))
391 | ((uint64_t)(u32Value & UINT32_C(0xff000000)) << 32);
392 }
393
394 Log(("ioapic: IOREDTBL%u %#018llx -> %#018llx\n", idxIoRedTbl, pThis->ioredtbl[idxIoRedTbl], u64NewValue));
395 pThis->ioredtbl[idxIoRedTbl] = u64NewValue;
396
397 ioapic_service(pThis);
398 }
399 /*
400 * Read-only or unknown registers. Log it.
401 */
402 else if (uIoRegSel == 1)
403 Log(("ioapic: Attempt to write (%#x) to IOAPICVER.\n", u32Value));
404 else if (uIoRegSel == 2)
405 Log(("ioapic: Attempt to write (%#x) to IOAPICARB.\n", u32Value));
406 else
407 Log(("ioapic: Attempt to write (%#x) to register %#x.\n", u32Value, uIoRegSel));
408
409 return rc;
410}
411
412
413PDMBOTHCBDECL(int) ioapicMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
414{
415 PIOAPIC pThis = PDMINS_2_DATA(pDevIns, PIOAPIC);
416 IOAPIC_LOCK(pThis, VINF_IOM_R3_MMIO_READ);
417
418 STAM_COUNTER_INC(&CTXSUFF(pThis->StatMMIORead));
419
420 /*
421 * Pass it on to the register read handlers.
422 * (See 0xff comments in ioapicMMIOWrite.)
423 */
424 int rc;
425 uint32_t offReg = GCPhysAddr & 0xff;
426 if (offReg == 0)
427 rc = ioapic_IoRegSel_r(pThis, (uint32_t *)pv);
428 else if (offReg == 0x10)
429 rc = ioapic_IoWin_r(pThis, (uint32_t *)pv);
430 else
431 {
432 Log(("ioapicMMIORead: Invalid access: offReg=%#x\n", offReg));
433 rc = VINF_IOM_MMIO_UNUSED_FF;
434 }
435 Log3(("ioapicMMIORead: @%#x -> %#x %Rrc\n", offReg, *(uint32_t *)pv, rc));
436
437 IOAPIC_UNLOCK(pThis);
438 return rc;
439}
440
441PDMBOTHCBDECL(int) ioapicMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
442{
443 PIOAPIC pThis = PDMINS_2_DATA(pDevIns, PIOAPIC);
444
445 STAM_COUNTER_INC(&CTXSUFF(pThis->StatMMIOWrite));
446 IOAPIC_LOCK(pThis, VINF_IOM_R3_MMIO_WRITE);
447
448 /*
449 * Fetch the value.
450 *
451 * We've told IOM to only give us DWORD accesses. Observations on AMD
452 * indicates that unaligned writes get their missing bytes written as zero.
453 */
454 Assert(!(GCPhysAddr & 3)); Assert(cb == 4);
455 uint32_t u32Value = *(uint32_t const *)pv;
456
457 /*
458 * The 0xff mask is because we don't really implement the APICBASE register
459 * in the PIIX3, so if the guest tries to relocate the IOAPIC via PIIX3 we
460 * won't know. The I/O APIC address is on the form FEC0xy00h, where xy is
461 * programmable. Masking 0xff means we cover the y. The x would require
462 * reregistering MMIO memory, which means the guest is out of luck there.
463 */
464 int rc;
465 uint32_t offReg = GCPhysAddr & 0xff;
466 if (offReg == 0)
467 rc = ioapic_IoRegSel_w(pThis, u32Value);
468 else if (offReg == 0x10)
469 rc = ioapic_IoWin_w(pThis, u32Value);
470 else
471 {
472 Log(("ioapicMMIOWrite: Invalid access: offReg=%#x u32Value=%#x\n", offReg, u32Value));
473 rc = VINF_SUCCESS;
474 }
475 Log3(("ioapicMMIOWrite: @%#x := %#x %Rrc\n", offReg, u32Value, rc));
476
477 IOAPIC_UNLOCK(pThis);
478 return rc;
479}
480
481PDMBOTHCBDECL(void) ioapicSetIrq(PPDMDEVINS pDevIns, int iIrq, int iLevel, uint32_t uTagSrc)
482{
483 /* PDM lock is taken here; */ /** @todo add assertion */
484 PIOAPIC pThis = PDMINS_2_DATA(pDevIns, PIOAPIC);
485 STAM_COUNTER_INC(&pThis->CTXSUFF(StatSetIrq));
486 LogFlow(("ioapicSetIrq: iIrq=%d iLevel=%d uTagSrc=%#x\n", iIrq, iLevel, uTagSrc));
487 ioapic_set_irq(pThis, iIrq, iLevel, uTagSrc);
488}
489
490PDMBOTHCBDECL(void) ioapicSendMsi(PPDMDEVINS pDevIns, RTGCPHYS GCAddr, uint32_t uValue, uint32_t uTagSrc)
491{
492 PIOAPIC pThis = PDMINS_2_DATA(pDevIns, PIOAPIC);
493
494 LogFlow(("ioapicSendMsi: Address=%p uValue=%\n", GCAddr, uValue));
495
496 uint8_t dest = (GCAddr & VBOX_MSI_ADDR_DEST_ID_MASK) >> VBOX_MSI_ADDR_DEST_ID_SHIFT;
497 uint8_t vector_num = (uValue & VBOX_MSI_DATA_VECTOR_MASK) >> VBOX_MSI_DATA_VECTOR_SHIFT;
498 uint8_t dest_mode = (GCAddr >> VBOX_MSI_ADDR_DEST_MODE_SHIFT) & 0x1;
499 uint8_t trigger_mode = (uValue >> VBOX_MSI_DATA_TRIGGER_SHIFT) & 0x1;
500 uint8_t delivery_mode = (uValue >> VBOX_MSI_DATA_DELIVERY_MODE_SHIFT) & 0x7;
501#if 0
502 /*
503 * This bit indicates whether the message should be directed to the
504 * processor with the lowest interrupt priority among
505 * processors that can receive the interrupt, ignored ATM.
506 */
507 uint8_t redir_hint = (GCAddr >> VBOX_MSI_ADDR_REDIRECTION_SHIFT) & 0x1;
508#endif
509 int rc = pThis->CTX_SUFF(pIoApicHlp)->pfnApicBusDeliver(pDevIns,
510 dest,
511 dest_mode,
512 delivery_mode,
513 vector_num,
514 0 /* polarity, n/a */,
515 trigger_mode,
516 uTagSrc);
517 /* We must be sure that attempts to reschedule in R3
518 never get here */
519 Assert(rc == VINF_SUCCESS);
520}
521
522#ifdef IN_RING3
523
524/** @interface_method_impl{DBGFREGDESC,pfnGet} */
525static DECLCALLBACK(int) ioapicDbgReg_IoRegSel_r(void *pvUser, PCDBGFREGDESC pDesc, PDBGFREGVAL pValue)
526{
527 return ioapic_IoRegSel_r(PDMINS_2_DATA((PPDMDEVINS)pvUser, PIOAPIC), &pValue->u32);
528}
529
530/** @interface_method_impl{DBGFREGDESC,pfnSet} */
531static DECLCALLBACK(int) ioapicDbgReg_IoRegSel_w(void *pvUser, PCDBGFREGDESC pDesc, PCDBGFREGVAL pValue, PCDBGFREGVAL pfMask)
532{
533 return ioapic_IoRegSel_w(PDMINS_2_DATA((PPDMDEVINS)pvUser, PIOAPIC), pValue->u8);
534}
535
536/** @interface_method_impl{DBGFREGDESC,pfnGet} */
537static DECLCALLBACK(int) ioapicDbgReg_IoWin_r(void *pvUser, PCDBGFREGDESC pDesc, PDBGFREGVAL pValue)
538{
539 return ioapic_IoWin_r(PDMINS_2_DATA((PPDMDEVINS)pvUser, PIOAPIC), &pValue->u32);
540}
541
542/** @interface_method_impl{DBGFREGDESC,pfnSet} */
543static DECLCALLBACK(int) ioapicDbgReg_IoWin_w(void *pvUser, PCDBGFREGDESC pDesc, PCDBGFREGVAL pValue, PCDBGFREGVAL pfMask)
544{
545 return ioapic_IoWin_w(PDMINS_2_DATA((PPDMDEVINS)pvUser, PIOAPIC), pValue->u32);
546}
547
548/** @interface_method_impl{DBGFREGDESC,pfnGet} */
549static DECLCALLBACK(int) ioapicDbgReg_IoApicVer_r(void *pvUser, PCDBGFREGDESC pDesc, PDBGFREGVAL pValue)
550{
551 return ioapic_IoApicVer_r(PDMINS_2_DATA((PPDMDEVINS)pvUser, PIOAPIC), &pValue->u32);
552}
553
554/** @interface_method_impl{DBGFREGDESC,pfnGet} */
555static DECLCALLBACK(int) ioapicDbgReg_IoApicArb_r(void *pvUser, PCDBGFREGDESC pDesc, PDBGFREGVAL pValue)
556{
557 return ioapic_IoApicArb_r(PDMINS_2_DATA((PPDMDEVINS)pvUser, PIOAPIC), &pValue->u32);
558}
559
560/** @interface_method_impl{DBGFREGDESC,pfnGet} */
561static DECLCALLBACK(int) ioapicDbgReg_IoRedRblN_r(void *pvUser, PCDBGFREGDESC pDesc, PDBGFREGVAL pValue)
562{
563 PIOAPIC pThis = PDMINS_2_DATA((PPDMDEVINS)pvUser, PIOAPIC);
564 pValue->u64 = pThis->ioredtbl[pDesc->offRegister];
565 return VINF_SUCCESS;
566}
567
568/** @interface_method_impl{DBGFREGDESC,pfnSet} */
569static DECLCALLBACK(int) ioapicDbgReg_IoRedRblN_w(void *pvUser, PCDBGFREGDESC pDesc, PCDBGFREGVAL pValue, PCDBGFREGVAL pfMask)
570{
571 PIOAPIC pThis = PDMINS_2_DATA((PPDMDEVINS)pvUser, PIOAPIC);
572 pThis->ioredtbl[pDesc->offRegister] = pValue->u64 | (~pfMask->u64 &pThis->ioredtbl[pDesc->offRegister]);
573 return VINF_SUCCESS;
574}
575
576/** Register descriptors for DBGF. */
577static DBGFREGDESC const g_aRegDesc[] =
578{
579 { "ioregsel", DBGFREG_END, DBGFREGVALTYPE_U8, 0, 0, ioapicDbgReg_IoRegSel_r, ioapicDbgReg_IoRegSel_w, NULL, NULL },
580 { "iowin", DBGFREG_END, DBGFREGVALTYPE_U32, 0, 0, ioapicDbgReg_IoWin_r, ioapicDbgReg_IoWin_w, NULL, NULL },
581 { "ioapicver", DBGFREG_END, DBGFREGVALTYPE_U32, DBGFREG_FLAGS_READ_ONLY, 0, ioapicDbgReg_IoApicVer_r, NULL, NULL, NULL },
582 { "ioapicarb", DBGFREG_END, DBGFREGVALTYPE_U32, DBGFREG_FLAGS_READ_ONLY, 0, ioapicDbgReg_IoApicArb_r, NULL, NULL, NULL },
583 { "ioredtbl0", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 0, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, NULL },
584 { "ioredtbl1", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 1, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, NULL },
585 { "ioredtbl2", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 2, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, NULL },
586 { "ioredtbl3", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 3, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, NULL },
587 { "ioredtbl4", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 4, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, NULL },
588 { "ioredtbl5", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 5, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, NULL },
589 { "ioredtbl6", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 6, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, NULL },
590 { "ioredtbl7", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 7, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, NULL },
591 { "ioredtbl8", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 8, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, NULL },
592 { "ioredtbl9", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 9, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, NULL },
593 { "ioredtbl10", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 10, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, NULL },
594 { "ioredtbl11", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 11, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, NULL },
595 { "ioredtbl12", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 12, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, NULL },
596 { "ioredtbl13", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 13, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, NULL },
597 { "ioredtbl14", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 14, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, NULL },
598 { "ioredtbl15", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 15, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, NULL },
599 { "ioredtbl16", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 16, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, NULL },
600 { "ioredtbl17", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 17, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, NULL },
601 { "ioredtbl18", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 18, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, NULL },
602 { "ioredtbl19", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 19, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, NULL },
603 { "ioredtbl20", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 20, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, NULL },
604 { "ioredtbl21", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 21, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, NULL },
605 { "ioredtbl22", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 22, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, NULL },
606 { "ioredtbl23", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 23, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, NULL },
607 DBGFREGDESC_TERMINATOR()
608};
609
610
611/**
612 * Info handler, device version. Dumps I/O APIC state.
613 *
614 * @param pDevIns Device instance which registered the info.
615 * @param pHlp Callback functions for doing output.
616 * @param pszArgs Argument string. Optional and specific to the handler.
617 */
618static DECLCALLBACK(void) ioapicInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
619{
620 PIOAPIC pThis = PDMINS_2_DATA(pDevIns, PIOAPIC);
621 uint32_t uVal;
622
623 pHlp->pfnPrintf(pHlp, "I/O APIC at %#010x:\n", 0xfec00000);
624
625 ioapic_IoApicId_r(pThis, &uVal);
626 pHlp->pfnPrintf(pHlp, " IOAPICID : %#010x\n", uVal);
627 pHlp->pfnPrintf(pHlp, " APIC ID = %#04x\n", (uVal >> 24) & 0xff);
628
629 ioapic_IoApicVer_r(pThis, &uVal);
630 unsigned iLastRedir = RT_BYTE3(uVal);
631 pHlp->pfnPrintf(pHlp, " IOAPICVER : %#010x\n", uVal);
632 pHlp->pfnPrintf(pHlp, " version = %#04x\n", uVal & 0xff);
633 pHlp->pfnPrintf(pHlp, " redirs = %u\n", iLastRedir + 1);
634
635 ioapic_IoApicArb_r(pThis, &uVal);
636 pHlp->pfnPrintf(pHlp, " arb ID = %#010x\n", RT_BYTE4(uVal));
637 pHlp->pfnPrintf(pHlp, " IOAPICARB : %#08x\n", uVal);
638
639 Assert(sizeof(pThis->ioredtbl) / sizeof(pThis->ioredtbl[0]) > iLastRedir);
640 pHlp->pfnPrintf(pHlp, "I/O redirection table\n");
641 pHlp->pfnPrintf(pHlp, " idx dst_mode dst_addr mask trigger rirr polarity dlvr_st dlvr_mode vector\n");
642 for (unsigned i = 0; i <= iLastRedir; ++i)
643 {
644 static const char * const s_apszDModes[] =
645 {
646 "Fixed ", "LowPri", "SMI ", "Resrvd", "NMI ", "INIT ", "Resrvd", "ExtINT"
647 };
648
649 pHlp->pfnPrintf(pHlp, " %02d %s %02x %d %s %d %s %s %s %3d (%016llx)\n",
650 i,
651 pThis->ioredtbl[i] & RT_BIT(11) ? "log " : "phys", /* dest mode */
652 (int)(pThis->ioredtbl[i] >> 56), /* dest addr */
653 (int)(pThis->ioredtbl[i] >> 16) & 1, /* mask */
654 pThis->ioredtbl[i] & RT_BIT(15) ? "level" : "edge ", /* trigger */
655 (int)(pThis->ioredtbl[i] >> 14) & 1, /* remote IRR */
656 pThis->ioredtbl[i] & RT_BIT(13) ? "activelo" : "activehi", /* polarity */
657 pThis->ioredtbl[i] & RT_BIT(12) ? "pend" : "idle", /* delivery status */
658 s_apszDModes[(pThis->ioredtbl[i] >> 8) & 0x07], /* delivery mode */
659 (int)pThis->ioredtbl[i] & 0xff, /* vector */
660 pThis->ioredtbl[i] /* entire register */
661 );
662 }
663}
664
665/**
666 * @copydoc FNSSMDEVSAVEEXEC
667 */
668static DECLCALLBACK(int) ioapicSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
669{
670 PIOAPIC pThis = PDMINS_2_DATA(pDevIns, PIOAPIC);
671
672 SSMR3PutU8(pSSM, pThis->id);
673 SSMR3PutU8(pSSM, pThis->ioregsel);
674 for (unsigned i = 0; i < IOAPIC_NUM_PINS; i++)
675 SSMR3PutU64(pSSM, pThis->ioredtbl[i]);
676
677 return VINF_SUCCESS;
678}
679
680/**
681 * @copydoc FNSSMDEVLOADEXEC
682 */
683static DECLCALLBACK(int) ioapicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
684{
685 PIOAPIC pThis = PDMINS_2_DATA(pDevIns, PIOAPIC);
686 if (uVersion != 1)
687 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
688
689 SSMR3GetU8(pSSM, &pThis->id);
690 SSMR3GetU8(pSSM, &pThis->ioregsel);
691 for (unsigned i = 0; i < IOAPIC_NUM_PINS; i++)
692 SSMR3GetU64(pSSM, &pThis->ioredtbl[i]);
693
694 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
695 return VINF_SUCCESS;
696}
697
698/**
699 * @copydoc FNPDMDEVRESET
700 */
701static DECLCALLBACK(void) ioapicReset(PPDMDEVINS pDevIns)
702{
703 PIOAPIC pThis = PDMINS_2_DATA(pDevIns, PIOAPIC);
704 pThis->pIoApicHlpR3->pfnLock(pDevIns, VERR_INTERNAL_ERROR);
705
706 pThis->id = pThis->cCpus;
707 pThis->ioregsel = 0;
708 pThis->irr = 0;
709 for (unsigned i = 0; i < IOAPIC_NUM_PINS; i++)
710 {
711 pThis->ioredtbl[i] = 1 << 16; /* mask LVT */
712 pThis->auTagSrc[i] = 0;
713 }
714
715 IOAPIC_UNLOCK(pThis);
716}
717
718/**
719 * @copydoc FNPDMDEVRELOCATE
720 */
721static DECLCALLBACK(void) ioapicRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
722{
723 PIOAPIC pThis = PDMINS_2_DATA(pDevIns, PIOAPIC);
724 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
725 pThis->pIoApicHlpRC = pThis->pIoApicHlpR3->pfnGetRCHelpers(pDevIns);
726}
727
728/**
729 * @copydoc FNPDMDEVCONSTRUCT
730 */
731static DECLCALLBACK(int) ioapicConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
732{
733 PIOAPIC pThis = PDMINS_2_DATA(pDevIns, PIOAPIC);
734 Assert(iInstance == 0);
735
736 /*
737 * Initialize the state data.
738 */
739 pThis->pDevInsR3 = pDevIns;
740 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
741 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
742 /* (the rest is done by the reset call at the end) */
743
744 /* PDM provides locking via the IOAPIC helpers. */
745 int rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
746 AssertRCReturn(rc, rc);
747
748 /*
749 * Validate and read the configuration.
750 */
751 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "NumCPUs|RZEnabled", "");
752
753 uint32_t cCpus;
754 rc = CFGMR3QueryU32Def(pCfg, "NumCPUs", &cCpus, 1);
755 if (RT_FAILURE(rc))
756 return PDMDEV_SET_ERROR(pDevIns, rc,
757 N_("Configuration error: Failed to query integer value \"NumCPUs\""));
758 if (cCpus > UINT8_MAX - 2) /* ID 255 is broadcast and the IO-APIC needs one (ID=cCpus). */
759 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
760 N_("Configuration error: Max %u CPUs, %u specified"), UINT8_MAX - 1, cCpus);
761 pThis->cCpus = (uint8_t)cCpus;
762
763 bool fRZEnabled;
764 rc = CFGMR3QueryBoolDef(pCfg, "RZEnabled", &fRZEnabled, true);
765 if (RT_FAILURE(rc))
766 return PDMDEV_SET_ERROR(pDevIns, rc,
767 N_("Configuration error: Failed to query boolean value \"RZEnabled\""));
768
769 Log(("IOAPIC: cCpus=%u fRZEnabled=%RTbool\n", cCpus, fRZEnabled));
770
771 /*
772 * Register the IOAPIC and get helpers.
773 */
774 PDMIOAPICREG IoApicReg;
775 IoApicReg.u32Version = PDM_IOAPICREG_VERSION;
776 IoApicReg.pfnSetIrqR3 = ioapicSetIrq;
777 IoApicReg.pszSetIrqRC = fRZEnabled ? "ioapicSetIrq" : NULL;
778 IoApicReg.pszSetIrqR0 = fRZEnabled ? "ioapicSetIrq" : NULL;
779 IoApicReg.pfnSendMsiR3 = ioapicSendMsi;
780 IoApicReg.pszSendMsiRC = fRZEnabled ? "ioapicSendMsi" : NULL;
781 IoApicReg.pszSendMsiR0 = fRZEnabled ? "ioapicSendMsi" : NULL;
782
783 rc = PDMDevHlpIOAPICRegister(pDevIns, &IoApicReg, &pThis->pIoApicHlpR3);
784 if (RT_FAILURE(rc))
785 {
786 AssertMsgFailed(("IOAPICRegister -> %Rrc\n", rc));
787 return rc;
788 }
789
790 /*
791 * Register MMIO callbacks and saved state.
792 * Note! The write ZEROing was observed on a real AMD system.
793 */
794 rc = PDMDevHlpMMIORegister(pDevIns, UINT32_C(0xfec00000), 0x1000, pThis,
795 IOMMMIO_FLAGS_READ_DWORD | IOMMMIO_FLAGS_WRITE_DWORD_ZEROED,
796 ioapicMMIOWrite, ioapicMMIORead, "I/O APIC Memory");
797 if (RT_FAILURE(rc))
798 return rc;
799
800 if (fRZEnabled)
801 {
802 pThis->pIoApicHlpRC = pThis->pIoApicHlpR3->pfnGetRCHelpers(pDevIns);
803 rc = PDMDevHlpMMIORegisterRC(pDevIns, UINT32_C(0xfec00000), 0x1000, NIL_RTRCPTR /*pvUser*/,
804 "ioapicMMIOWrite", "ioapicMMIORead");
805 AssertRCReturn(rc, rc);
806
807 pThis->pIoApicHlpR0 = pThis->pIoApicHlpR3->pfnGetR0Helpers(pDevIns);
808 rc = PDMDevHlpMMIORegisterR0(pDevIns, UINT32_C(0xfec00000), 0x1000, NIL_RTR0PTR /*pvUser*/,
809 "ioapicMMIOWrite", "ioapicMMIORead");
810 AssertRCReturn(rc, rc);
811 }
812
813 rc = PDMDevHlpSSMRegister(pDevIns, 1 /* version */, sizeof(*pThis), ioapicSaveExec, ioapicLoadExec);
814 if (RT_FAILURE(rc))
815 return rc;
816
817 /*
818 * Register debugger info callback.
819 */
820 PDMDevHlpDBGFInfoRegister(pDevIns, "ioapic", "Display I/O APIC state.", ioapicInfo);
821 rc = DBGFR3RegRegisterDevice(PDMDevHlpGetVM(pDevIns), g_aRegDesc, pDevIns, pDevIns->pReg->szName, pDevIns->iInstance);
822 AssertRC(rc);
823
824#ifdef VBOX_WITH_STATISTICS
825 /*
826 * Statistics.
827 */
828 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMMIOReadGC, STAMTYPE_COUNTER, "/Devices/IOAPIC/MMIOReadGC", STAMUNIT_OCCURENCES, "Number of IOAPIC MMIO reads in GC.");
829 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMMIOReadHC, STAMTYPE_COUNTER, "/Devices/IOAPIC/MMIOReadHC", STAMUNIT_OCCURENCES, "Number of IOAPIC MMIO reads in HC.");
830 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMMIOWriteGC, STAMTYPE_COUNTER, "/Devices/IOAPIC/MMIOWriteGC", STAMUNIT_OCCURENCES, "Number of IOAPIC MMIO writes in GC.");
831 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMMIOWriteHC, STAMTYPE_COUNTER, "/Devices/IOAPIC/MMIOWriteHC", STAMUNIT_OCCURENCES, "Number of IOAPIC MMIO writes in HC.");
832 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatSetIrqGC, STAMTYPE_COUNTER, "/Devices/IOAPIC/SetIrqGC", STAMUNIT_OCCURENCES, "Number of IOAPIC SetIrq calls in GC.");
833 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatSetIrqHC, STAMTYPE_COUNTER, "/Devices/IOAPIC/SetIrqHC", STAMUNIT_OCCURENCES, "Number of IOAPIC SetIrq calls in HC.");
834#endif
835
836 /*
837 * Reset the device state.
838 */
839 ioapicReset(pDevIns);
840
841 return VINF_SUCCESS;
842}
843
844/**
845 * IO APIC device registration structure.
846 */
847const PDMDEVREG g_DeviceIOAPIC =
848{
849 /* u32Version */
850 PDM_DEVREG_VERSION,
851 /* szName */
852 "ioapic",
853 /* szRCMod */
854 "VBoxDD2GC.gc",
855 /* szR0Mod */
856 "VBoxDD2R0.r0",
857 /* pszDescription */
858 "I/O Advanced Programmable Interrupt Controller (IO-APIC) Device",
859 /* fFlags */
860 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64 | PDM_DEVREG_FLAGS_PAE36 | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
861 /* fClass */
862 PDM_DEVREG_CLASS_PIC,
863 /* cMaxInstances */
864 1,
865 /* cbInstance */
866 sizeof(IOAPIC),
867 /* pfnConstruct */
868 ioapicConstruct,
869 /* pfnDestruct */
870 NULL,
871 /* pfnRelocate */
872 ioapicRelocate,
873 /* pfnIOCtl */
874 NULL,
875 /* pfnPowerOn */
876 NULL,
877 /* pfnReset */
878 ioapicReset,
879 /* pfnSuspend */
880 NULL,
881 /* pfnResume */
882 NULL,
883 /* pfnAttach */
884 NULL,
885 /* pfnDetach */
886 NULL,
887 /* pfnQueryInterface. */
888 NULL,
889 /* pfnInitComplete */
890 NULL,
891 /* pfnPowerOff */
892 NULL,
893 /* pfnSoftReset */
894 NULL,
895 /* u32VersionEnd */
896 PDM_DEVREG_VERSION
897};
898
899#endif /* IN_RING3 */
900#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
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