VirtualBox

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

Last change on this file since 53392 was 51373, checked in by vboxsync, 10 years ago

Fixed bad format specifier.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 34.7 KB
Line 
1/* $Id: DevIoApic.cpp 51373 2014-05-23 15:32:55Z 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=%u\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/** IOREDTBLn sub fields. */
577static DBGFREGSUBFIELD const g_aIoRedTblSubs[] =
578{
579 { "intvec", 0, 8, 0, 0, NULL, NULL },
580 { "delmode", 8, 3, 0, 0, NULL, NULL },
581 { "destmode", 11, 1, 0, 0, NULL, NULL },
582 { "delivs", 12, 1, 0, DBGFREGSUBFIELD_FLAGS_READ_ONLY, NULL, NULL },
583 { "intpol", 13, 1, 0, 0, NULL, NULL },
584 { "remoteirr", 14, 1, 0, DBGFREGSUBFIELD_FLAGS_READ_ONLY, NULL, NULL },
585 { "triggermode", 15, 1, 0, 0, NULL, NULL },
586 { "intmask", 16, 1, 0, 0, NULL, NULL },
587 { "dst", 56, 8, 0, 0, NULL, NULL },
588 DBGFREGSUBFIELD_TERMINATOR()
589};
590
591/** Register descriptors for DBGF. */
592static DBGFREGDESC const g_aRegDesc[] =
593{
594 { "ioregsel", DBGFREG_END, DBGFREGVALTYPE_U8, 0, 0, ioapicDbgReg_IoRegSel_r, ioapicDbgReg_IoRegSel_w, NULL, NULL },
595 { "iowin", DBGFREG_END, DBGFREGVALTYPE_U32, 0, 0, ioapicDbgReg_IoWin_r, ioapicDbgReg_IoWin_w, NULL, NULL },
596 { "ioapicver", DBGFREG_END, DBGFREGVALTYPE_U32, DBGFREG_FLAGS_READ_ONLY, 0, ioapicDbgReg_IoApicVer_r, NULL, NULL, NULL },
597 { "ioapicarb", DBGFREG_END, DBGFREGVALTYPE_U32, DBGFREG_FLAGS_READ_ONLY, 0, ioapicDbgReg_IoApicArb_r, NULL, NULL, NULL },
598 { "ioredtbl0", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 0, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, &g_aIoRedTblSubs[0] },
599 { "ioredtbl1", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 1, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, &g_aIoRedTblSubs[0] },
600 { "ioredtbl2", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 2, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, &g_aIoRedTblSubs[0] },
601 { "ioredtbl3", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 3, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, &g_aIoRedTblSubs[0] },
602 { "ioredtbl4", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 4, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, &g_aIoRedTblSubs[0] },
603 { "ioredtbl5", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 5, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, &g_aIoRedTblSubs[0] },
604 { "ioredtbl6", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 6, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, &g_aIoRedTblSubs[0] },
605 { "ioredtbl7", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 7, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, &g_aIoRedTblSubs[0] },
606 { "ioredtbl8", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 8, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, &g_aIoRedTblSubs[0] },
607 { "ioredtbl9", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 9, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, &g_aIoRedTblSubs[0] },
608 { "ioredtbl10", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 10, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, &g_aIoRedTblSubs[0] },
609 { "ioredtbl11", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 11, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, &g_aIoRedTblSubs[0] },
610 { "ioredtbl12", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 12, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, &g_aIoRedTblSubs[0] },
611 { "ioredtbl13", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 13, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, &g_aIoRedTblSubs[0] },
612 { "ioredtbl14", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 14, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, &g_aIoRedTblSubs[0] },
613 { "ioredtbl15", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 15, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, &g_aIoRedTblSubs[0] },
614 { "ioredtbl16", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 16, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, &g_aIoRedTblSubs[0] },
615 { "ioredtbl17", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 17, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, &g_aIoRedTblSubs[0] },
616 { "ioredtbl18", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 18, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, &g_aIoRedTblSubs[0] },
617 { "ioredtbl19", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 19, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, &g_aIoRedTblSubs[0] },
618 { "ioredtbl20", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 20, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, &g_aIoRedTblSubs[0] },
619 { "ioredtbl21", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 21, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, &g_aIoRedTblSubs[0] },
620 { "ioredtbl22", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 22, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, &g_aIoRedTblSubs[0] },
621 { "ioredtbl23", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 23, ioapicDbgReg_IoRedRblN_r, ioapicDbgReg_IoRedRblN_w, NULL, &g_aIoRedTblSubs[0] },
622 DBGFREGDESC_TERMINATOR()
623};
624
625
626/**
627 * Info handler, device version. Dumps I/O APIC state.
628 *
629 * @param pDevIns Device instance which registered the info.
630 * @param pHlp Callback functions for doing output.
631 * @param pszArgs Argument string. Optional and specific to the handler.
632 */
633static DECLCALLBACK(void) ioapicInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
634{
635 PIOAPIC pThis = PDMINS_2_DATA(pDevIns, PIOAPIC);
636 uint32_t uVal;
637
638 pHlp->pfnPrintf(pHlp, "I/O APIC at %#010x:\n", 0xfec00000);
639
640 ioapic_IoApicId_r(pThis, &uVal);
641 pHlp->pfnPrintf(pHlp, " IOAPICID : %#010x\n", uVal);
642 pHlp->pfnPrintf(pHlp, " APIC ID = %#04x\n", (uVal >> 24) & 0xff);
643
644 ioapic_IoApicVer_r(pThis, &uVal);
645 unsigned iLastRedir = RT_BYTE3(uVal);
646 pHlp->pfnPrintf(pHlp, " IOAPICVER : %#010x\n", uVal);
647 pHlp->pfnPrintf(pHlp, " version = %#04x\n", uVal & 0xff);
648 pHlp->pfnPrintf(pHlp, " redirs = %u\n", iLastRedir + 1);
649
650 ioapic_IoApicArb_r(pThis, &uVal);
651 pHlp->pfnPrintf(pHlp, " arb ID = %#010x\n", RT_BYTE4(uVal));
652 pHlp->pfnPrintf(pHlp, " IOAPICARB : %#08x\n", uVal);
653
654 Assert(sizeof(pThis->ioredtbl) / sizeof(pThis->ioredtbl[0]) > iLastRedir);
655 pHlp->pfnPrintf(pHlp, "I/O redirection table\n");
656 pHlp->pfnPrintf(pHlp, " idx dst_mode dst_addr mask trigger rirr polarity dlvr_st dlvr_mode vector\n");
657 for (unsigned i = 0; i <= iLastRedir; ++i)
658 {
659 static const char * const s_apszDModes[] =
660 {
661 "Fixed ", "LowPri", "SMI ", "Resrvd", "NMI ", "INIT ", "Resrvd", "ExtINT"
662 };
663
664 pHlp->pfnPrintf(pHlp, " %02d %s %02x %d %s %d %s %s %s %3d (%016llx)\n",
665 i,
666 pThis->ioredtbl[i] & RT_BIT(11) ? "log " : "phys", /* dest mode */
667 (int)(pThis->ioredtbl[i] >> 56), /* dest addr */
668 (int)(pThis->ioredtbl[i] >> 16) & 1, /* mask */
669 pThis->ioredtbl[i] & RT_BIT(15) ? "level" : "edge ", /* trigger */
670 (int)(pThis->ioredtbl[i] >> 14) & 1, /* remote IRR */
671 pThis->ioredtbl[i] & RT_BIT(13) ? "activelo" : "activehi", /* polarity */
672 pThis->ioredtbl[i] & RT_BIT(12) ? "pend" : "idle", /* delivery status */
673 s_apszDModes[(pThis->ioredtbl[i] >> 8) & 0x07], /* delivery mode */
674 (int)pThis->ioredtbl[i] & 0xff, /* vector */
675 pThis->ioredtbl[i] /* entire register */
676 );
677 }
678}
679
680/**
681 * @copydoc FNSSMDEVSAVEEXEC
682 */
683static DECLCALLBACK(int) ioapicSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
684{
685 PIOAPIC pThis = PDMINS_2_DATA(pDevIns, PIOAPIC);
686
687 SSMR3PutU8(pSSM, pThis->id);
688 SSMR3PutU8(pSSM, pThis->ioregsel);
689 for (unsigned i = 0; i < IOAPIC_NUM_PINS; i++)
690 SSMR3PutU64(pSSM, pThis->ioredtbl[i]);
691
692 return VINF_SUCCESS;
693}
694
695/**
696 * @copydoc FNSSMDEVLOADEXEC
697 */
698static DECLCALLBACK(int) ioapicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
699{
700 PIOAPIC pThis = PDMINS_2_DATA(pDevIns, PIOAPIC);
701 if (uVersion != 1)
702 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
703
704 SSMR3GetU8(pSSM, &pThis->id);
705 SSMR3GetU8(pSSM, &pThis->ioregsel);
706 for (unsigned i = 0; i < IOAPIC_NUM_PINS; i++)
707 SSMR3GetU64(pSSM, &pThis->ioredtbl[i]);
708
709 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
710 return VINF_SUCCESS;
711}
712
713/**
714 * @copydoc FNPDMDEVRESET
715 */
716static DECLCALLBACK(void) ioapicReset(PPDMDEVINS pDevIns)
717{
718 PIOAPIC pThis = PDMINS_2_DATA(pDevIns, PIOAPIC);
719 pThis->pIoApicHlpR3->pfnLock(pDevIns, VERR_INTERNAL_ERROR);
720
721 pThis->id = pThis->cCpus;
722 pThis->ioregsel = 0;
723 pThis->irr = 0;
724 for (unsigned i = 0; i < IOAPIC_NUM_PINS; i++)
725 {
726 pThis->ioredtbl[i] = 1 << 16; /* mask LVT */
727 pThis->auTagSrc[i] = 0;
728 }
729
730 IOAPIC_UNLOCK(pThis);
731}
732
733/**
734 * @copydoc FNPDMDEVRELOCATE
735 */
736static DECLCALLBACK(void) ioapicRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
737{
738 PIOAPIC pThis = PDMINS_2_DATA(pDevIns, PIOAPIC);
739 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
740 pThis->pIoApicHlpRC = pThis->pIoApicHlpR3->pfnGetRCHelpers(pDevIns);
741}
742
743/**
744 * @copydoc FNPDMDEVCONSTRUCT
745 */
746static DECLCALLBACK(int) ioapicConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
747{
748 PIOAPIC pThis = PDMINS_2_DATA(pDevIns, PIOAPIC);
749 Assert(iInstance == 0);
750
751 /*
752 * Initialize the state data.
753 */
754 pThis->pDevInsR3 = pDevIns;
755 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
756 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
757 /* (the rest is done by the reset call at the end) */
758
759 /* PDM provides locking via the IOAPIC helpers. */
760 int rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
761 AssertRCReturn(rc, rc);
762
763 /*
764 * Validate and read the configuration.
765 */
766 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "NumCPUs|RZEnabled", "");
767
768 uint32_t cCpus;
769 rc = CFGMR3QueryU32Def(pCfg, "NumCPUs", &cCpus, 1);
770 if (RT_FAILURE(rc))
771 return PDMDEV_SET_ERROR(pDevIns, rc,
772 N_("Configuration error: Failed to query integer value \"NumCPUs\""));
773 if (cCpus > UINT8_MAX - 2) /* ID 255 is broadcast and the IO-APIC needs one (ID=cCpus). */
774 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
775 N_("Configuration error: Max %u CPUs, %u specified"), UINT8_MAX - 1, cCpus);
776 pThis->cCpus = (uint8_t)cCpus;
777
778 bool fRZEnabled;
779 rc = CFGMR3QueryBoolDef(pCfg, "RZEnabled", &fRZEnabled, true);
780 if (RT_FAILURE(rc))
781 return PDMDEV_SET_ERROR(pDevIns, rc,
782 N_("Configuration error: Failed to query boolean value \"RZEnabled\""));
783
784 Log(("IOAPIC: cCpus=%u fRZEnabled=%RTbool\n", cCpus, fRZEnabled));
785
786 /*
787 * Register the IOAPIC and get helpers.
788 */
789 PDMIOAPICREG IoApicReg;
790 IoApicReg.u32Version = PDM_IOAPICREG_VERSION;
791 IoApicReg.pfnSetIrqR3 = ioapicSetIrq;
792 IoApicReg.pszSetIrqRC = fRZEnabled ? "ioapicSetIrq" : NULL;
793 IoApicReg.pszSetIrqR0 = fRZEnabled ? "ioapicSetIrq" : NULL;
794 IoApicReg.pfnSendMsiR3 = ioapicSendMsi;
795 IoApicReg.pszSendMsiRC = fRZEnabled ? "ioapicSendMsi" : NULL;
796 IoApicReg.pszSendMsiR0 = fRZEnabled ? "ioapicSendMsi" : NULL;
797
798 rc = PDMDevHlpIOAPICRegister(pDevIns, &IoApicReg, &pThis->pIoApicHlpR3);
799 if (RT_FAILURE(rc))
800 {
801 AssertMsgFailed(("IOAPICRegister -> %Rrc\n", rc));
802 return rc;
803 }
804
805 /*
806 * Register MMIO callbacks and saved state.
807 * Note! The write ZEROing was observed on a real AMD system.
808 */
809 rc = PDMDevHlpMMIORegister(pDevIns, UINT32_C(0xfec00000), 0x1000, pThis,
810 IOMMMIO_FLAGS_READ_DWORD | IOMMMIO_FLAGS_WRITE_DWORD_ZEROED,
811 ioapicMMIOWrite, ioapicMMIORead, "I/O APIC Memory");
812 if (RT_FAILURE(rc))
813 return rc;
814
815 if (fRZEnabled)
816 {
817 pThis->pIoApicHlpRC = pThis->pIoApicHlpR3->pfnGetRCHelpers(pDevIns);
818 rc = PDMDevHlpMMIORegisterRC(pDevIns, UINT32_C(0xfec00000), 0x1000, NIL_RTRCPTR /*pvUser*/,
819 "ioapicMMIOWrite", "ioapicMMIORead");
820 AssertRCReturn(rc, rc);
821
822 pThis->pIoApicHlpR0 = pThis->pIoApicHlpR3->pfnGetR0Helpers(pDevIns);
823 rc = PDMDevHlpMMIORegisterR0(pDevIns, UINT32_C(0xfec00000), 0x1000, NIL_RTR0PTR /*pvUser*/,
824 "ioapicMMIOWrite", "ioapicMMIORead");
825 AssertRCReturn(rc, rc);
826 }
827
828 rc = PDMDevHlpSSMRegister(pDevIns, 1 /* version */, sizeof(*pThis), ioapicSaveExec, ioapicLoadExec);
829 if (RT_FAILURE(rc))
830 return rc;
831
832 /*
833 * Register debugger info callback.
834 */
835 rc = PDMDevHlpDBGFInfoRegister(pDevIns, "ioapic", "Display I/O APIC state.", ioapicInfo); AssertRC(rc);
836 rc = PDMDevHlpDBGFRegRegister(pDevIns, g_aRegDesc); AssertRC(rc);
837
838#ifdef VBOX_WITH_STATISTICS
839 /*
840 * Statistics.
841 */
842 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMMIOReadGC, STAMTYPE_COUNTER, "/Devices/IOAPIC/MMIOReadGC", STAMUNIT_OCCURENCES, "Number of IOAPIC MMIO reads in GC.");
843 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMMIOReadHC, STAMTYPE_COUNTER, "/Devices/IOAPIC/MMIOReadHC", STAMUNIT_OCCURENCES, "Number of IOAPIC MMIO reads in HC.");
844 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMMIOWriteGC, STAMTYPE_COUNTER, "/Devices/IOAPIC/MMIOWriteGC", STAMUNIT_OCCURENCES, "Number of IOAPIC MMIO writes in GC.");
845 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMMIOWriteHC, STAMTYPE_COUNTER, "/Devices/IOAPIC/MMIOWriteHC", STAMUNIT_OCCURENCES, "Number of IOAPIC MMIO writes in HC.");
846 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatSetIrqGC, STAMTYPE_COUNTER, "/Devices/IOAPIC/SetIrqGC", STAMUNIT_OCCURENCES, "Number of IOAPIC SetIrq calls in GC.");
847 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatSetIrqHC, STAMTYPE_COUNTER, "/Devices/IOAPIC/SetIrqHC", STAMUNIT_OCCURENCES, "Number of IOAPIC SetIrq calls in HC.");
848#endif
849
850 /*
851 * Reset the device state.
852 */
853 ioapicReset(pDevIns);
854
855 return VINF_SUCCESS;
856}
857
858/**
859 * IO APIC device registration structure.
860 */
861const PDMDEVREG g_DeviceIOAPIC =
862{
863 /* u32Version */
864 PDM_DEVREG_VERSION,
865 /* szName */
866 "ioapic",
867 /* szRCMod */
868 "VBoxDD2GC.gc",
869 /* szR0Mod */
870 "VBoxDD2R0.r0",
871 /* pszDescription */
872 "I/O Advanced Programmable Interrupt Controller (IO-APIC) Device",
873 /* fFlags */
874 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,
875 /* fClass */
876 PDM_DEVREG_CLASS_PIC,
877 /* cMaxInstances */
878 1,
879 /* cbInstance */
880 sizeof(IOAPIC),
881 /* pfnConstruct */
882 ioapicConstruct,
883 /* pfnDestruct */
884 NULL,
885 /* pfnRelocate */
886 ioapicRelocate,
887 /* pfnMemSetup */
888 NULL,
889 /* pfnPowerOn */
890 NULL,
891 /* pfnReset */
892 ioapicReset,
893 /* pfnSuspend */
894 NULL,
895 /* pfnResume */
896 NULL,
897 /* pfnAttach */
898 NULL,
899 /* pfnDetach */
900 NULL,
901 /* pfnQueryInterface. */
902 NULL,
903 /* pfnInitComplete */
904 NULL,
905 /* pfnPowerOff */
906 NULL,
907 /* pfnSoftReset */
908 NULL,
909 /* u32VersionEnd */
910 PDM_DEVREG_VERSION
911};
912
913#endif /* IN_RING3 */
914#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