VirtualBox

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

Last change on this file since 60404 was 57358, checked in by vboxsync, 9 years ago

*: scm cleanup run.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use