VirtualBox

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

Last change on this file since 82041 was 82041, checked in by vboxsync, 5 years ago

PIC,APIC,IOAPIC,PDM: The PIC, APIC, and IOAPIC cannot have their ring-0 bits disabled or PDM will get interrupt handling all wrong (left todo). bugref:9218

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 60.4 KB
Line 
1/* $Id: DevIoApic.cpp 82041 2019-11-20 18:58:22Z vboxsync $ */
2/** @file
3 * IO APIC - Input/Output Advanced Programmable Interrupt Controller.
4 */
5
6/*
7 * Copyright (C) 2016-2019 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
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DEV_IOAPIC
23#include <VBox/log.h>
24#include <VBox/vmm/hm.h>
25#include <VBox/msi.h>
26#include <VBox/vmm/pdmdev.h>
27
28#include "VBoxDD.h"
29#include <iprt/x86.h>
30
31
32/*********************************************************************************************************************************
33* Defined Constants And Macros *
34*********************************************************************************************************************************/
35/** The current IO APIC saved state version. */
36#define IOAPIC_SAVED_STATE_VERSION 2
37/** The saved state version used by VirtualBox 5.0 and
38 * earlier. */
39#define IOAPIC_SAVED_STATE_VERSION_VBOX_50 1
40
41/** Implementation specified by the "Intel I/O Controller Hub 9
42 * (ICH9) Family" */
43#define IOAPIC_VERSION_ICH9 0x20
44/** Implementation specified by the "82093AA I/O Advanced Programmable Interrupt
45Controller" */
46#define IOAPIC_VERSION_82093AA 0x11
47
48/** The default MMIO base physical address. */
49#define IOAPIC_MMIO_BASE_PHYSADDR UINT64_C(0xfec00000)
50/** The size of the MMIO range. */
51#define IOAPIC_MMIO_SIZE X86_PAGE_4K_SIZE
52/** The mask for getting direct registers from physical address. */
53#define IOAPIC_MMIO_REG_MASK 0xff
54
55/** The number of interrupt input pins. */
56#define IOAPIC_NUM_INTR_PINS 24
57/** Maximum redirection entires. */
58#define IOAPIC_MAX_RTE_INDEX (IOAPIC_NUM_INTR_PINS - 1)
59/** Reduced RTEs used by SIO.A (82379AB). */
60#define IOAPIC_REDUCED_MAX_RTE_INDEX (16 - 1)
61
62/** Version register - Gets the version. */
63#define IOAPIC_VER_GET_VER(a_Reg) ((a_Reg) & 0xff)
64/** Version register - Gets the maximum redirection entry. */
65#define IOAPIC_VER_GET_MRE(a_Reg) (((a_Reg) >> 16) & 0xff)
66/** Version register - Gets whether Pin Assertion Register (PRQ) is
67 * supported. */
68#define IOAPIC_VER_HAS_PRQ(a_Reg) RT_BOOL((a_Reg) & RT_BIT_32(15))
69
70/** Index register - Valid write mask. */
71#define IOAPIC_INDEX_VALID_WRITE_MASK UINT32_C(0xff)
72
73/** Arbitration register - Gets the ID. */
74#define IOAPIC_ARB_GET_ID(a_Reg) ((a_Reg) >> 24 & 0xf)
75
76/** ID register - Gets the ID. */
77#define IOAPIC_ID_GET_ID(a_Reg) ((a_Reg) >> 24 & 0xff)
78
79/** Redirection table entry - Vector. */
80#define IOAPIC_RTE_VECTOR UINT64_C(0xff)
81/** Redirection table entry - Delivery mode. */
82#define IOAPIC_RTE_DELIVERY_MODE (RT_BIT_64(8) | RT_BIT_64(9) | RT_BIT_64(10))
83/** Redirection table entry - Destination mode. */
84#define IOAPIC_RTE_DEST_MODE RT_BIT_64(11)
85/** Redirection table entry - Delivery status. */
86#define IOAPIC_RTE_DELIVERY_STATUS RT_BIT_64(12)
87/** Redirection table entry - Interrupt input pin polarity. */
88#define IOAPIC_RTE_POLARITY RT_BIT_64(13)
89/** Redirection table entry - Remote IRR. */
90#define IOAPIC_RTE_REMOTE_IRR RT_BIT_64(14)
91/** Redirection table entry - Trigger Mode. */
92#define IOAPIC_RTE_TRIGGER_MODE RT_BIT_64(15)
93/** Redirection table entry - the mask bit number. */
94#define IOAPIC_RTE_MASK_BIT 16
95/** Redirection table entry - the mask. */
96#define IOAPIC_RTE_MASK RT_BIT_64(IOAPIC_RTE_MASK_BIT)
97/** Redirection table entry - Extended Destination ID. */
98#define IOAPIC_RTE_EXT_DEST_ID UINT64_C(0x00ff000000000000)
99/** Redirection table entry - Destination. */
100#define IOAPIC_RTE_DEST UINT64_C(0xff00000000000000)
101
102/** Redirection table entry - Gets the destination. */
103#define IOAPIC_RTE_GET_DEST(a_Reg) ((a_Reg) >> 56 & 0xff)
104/** Redirection table entry - Gets the mask flag. */
105#define IOAPIC_RTE_GET_MASK(a_Reg) (((a_Reg) >> IOAPIC_RTE_MASK_BIT) & 0x1)
106/** Redirection table entry - Checks whether it's masked. */
107#define IOAPIC_RTE_IS_MASKED(a_Reg) ((a_Reg) & IOAPIC_RTE_MASK)
108/** Redirection table entry - Gets the trigger mode. */
109#define IOAPIC_RTE_GET_TRIGGER_MODE(a_Reg) (((a_Reg) >> 15) & 0x1)
110/** Redirection table entry - Gets the remote IRR flag. */
111#define IOAPIC_RTE_GET_REMOTE_IRR(a_Reg) (((a_Reg) >> 14) & 0x1)
112/** Redirection table entry - Gets the interrupt pin polarity. */
113#define IOAPIC_RTE_GET_POLARITY(a_Reg) (((a_Reg) >> 13) & 0x1)
114/** Redirection table entry - Gets the delivery status. */
115#define IOAPIC_RTE_GET_DELIVERY_STATUS(a_Reg) (((a_Reg) >> 12) & 0x1)
116/** Redirection table entry - Gets the destination mode. */
117#define IOAPIC_RTE_GET_DEST_MODE(a_Reg) (((a_Reg) >> 11) & 0x1)
118/** Redirection table entry - Gets the delivery mode. */
119#define IOAPIC_RTE_GET_DELIVERY_MODE(a_Reg) (((a_Reg) >> 8) & 0x7)
120/** Redirection table entry - Gets the vector. */
121#define IOAPIC_RTE_GET_VECTOR(a_Reg) ((a_Reg) & IOAPIC_RTE_VECTOR)
122
123/** Redirection table entry - Valid write mask for 82093AA. */
124#define IOAPIC_RTE_VALID_WRITE_MASK_82093AA ( IOAPIC_RTE_DEST | IOAPIC_RTE_MASK | IOAPIC_RTE_TRIGGER_MODE \
125 | IOAPIC_RTE_POLARITY | IOAPIC_RTE_DEST_MODE | IOAPIC_RTE_DELIVERY_MODE \
126 | IOAPIC_RTE_VECTOR)
127/** Redirection table entry - Valid read mask for 82093AA. */
128#define IOAPIC_RTE_VALID_READ_MASK_82093AA ( IOAPIC_RTE_DEST | IOAPIC_RTE_MASK | IOAPIC_RTE_TRIGGER_MODE \
129 | IOAPIC_RTE_REMOTE_IRR | IOAPIC_RTE_POLARITY | IOAPIC_RTE_DELIVERY_STATUS \
130 | IOAPIC_RTE_DEST_MODE | IOAPIC_RTE_DELIVERY_MODE | IOAPIC_RTE_VECTOR)
131
132/** Redirection table entry - Valid write mask for ICH9. */
133/** @note The remote IRR bit has been reverted to read-only as it turns out the
134 * ICH9 spec. is wrong, see @bugref{8386#c46}. */
135#define IOAPIC_RTE_VALID_WRITE_MASK_ICH9 ( IOAPIC_RTE_DEST | IOAPIC_RTE_MASK | IOAPIC_RTE_TRIGGER_MODE \
136 /*| IOAPIC_RTE_REMOTE_IRR */| IOAPIC_RTE_POLARITY | IOAPIC_RTE_DEST_MODE \
137 | IOAPIC_RTE_DELIVERY_MODE | IOAPIC_RTE_VECTOR)
138/** Redirection table entry - Valid read mask (incl. ExtDestID) for ICH9. */
139#define IOAPIC_RTE_VALID_READ_MASK_ICH9 ( IOAPIC_RTE_DEST | IOAPIC_RTE_EXT_DEST_ID | IOAPIC_RTE_MASK \
140 | IOAPIC_RTE_TRIGGER_MODE | IOAPIC_RTE_REMOTE_IRR | IOAPIC_RTE_POLARITY \
141 | IOAPIC_RTE_DELIVERY_STATUS | IOAPIC_RTE_DEST_MODE | IOAPIC_RTE_DELIVERY_MODE \
142 | IOAPIC_RTE_VECTOR)
143
144/** Redirection table entry - Trigger mode edge. */
145#define IOAPIC_RTE_TRIGGER_MODE_EDGE 0
146/** Redirection table entry - Trigger mode level. */
147#define IOAPIC_RTE_TRIGGER_MODE_LEVEL 1
148/** Redirection table entry - Destination mode physical. */
149#define IOAPIC_RTE_DEST_MODE_PHYSICAL 0
150/** Redirection table entry - Destination mode logical. */
151#define IOAPIC_RTE_DEST_MODE_LOGICAL 1
152
153
154/** Index of indirect registers in the I/O APIC register table. */
155#define IOAPIC_INDIRECT_INDEX_ID 0x0
156#define IOAPIC_INDIRECT_INDEX_VERSION 0x1
157#define IOAPIC_INDIRECT_INDEX_ARB 0x2 /* Older I/O APIC only. */
158#define IOAPIC_INDIRECT_INDEX_REDIR_TBL_START 0x10 /* First valid RTE register index. */
159#define IOAPIC_INDIRECT_INDEX_RTE_END 0x3F /* Last valid RTE register index (24 RTEs). */
160#define IOAPIC_REDUCED_INDIRECT_INDEX_RTE_END 0x2F /* Last valid RTE register index (16 RTEs). */
161
162/** Offset of direct registers in the I/O APIC MMIO space. */
163#define IOAPIC_DIRECT_OFF_INDEX 0x00
164#define IOAPIC_DIRECT_OFF_DATA 0x10
165#define IOAPIC_DIRECT_OFF_EOI 0x40 /* Newer I/O APIC only. */
166
167/* Use PDM critsect for now for I/O APIC locking, see @bugref{8245#c121}. */
168#define IOAPIC_WITH_PDM_CRITSECT
169#ifdef IOAPIC_WITH_PDM_CRITSECT
170# define IOAPIC_LOCK(a_pDevIns, a_pThis, a_pThisCC, rcBusy) (a_pThisCC)->pIoApicHlp->pfnLock((a_pDevIns), (rcBusy))
171# define IOAPIC_UNLOCK(a_pDevIns, a_pThis, a_pThisCC) (a_pThisCC)->pIoApicHlp->pfnUnlock((a_pDevIns))
172#else
173# define IOAPIC_LOCK(a_pDevIns, a_pThis, a_pThisCC, rcBusy) PDMDevHlpCritSectEnter((a_pDevIns), &(a_pThis)->CritSect, (rcBusy))
174# define IOAPIC_UNLOCK(a_pDevIns, a_pThis, a_pThisCC) PDMDevHlpCritSectLeave((a_pDevIns), &(a_pThis)->CritSect)
175#endif
176
177
178/*********************************************************************************************************************************
179* Structures and Typedefs *
180*********************************************************************************************************************************/
181/**
182 * The shared I/O APIC device state.
183 */
184typedef struct IOAPIC
185{
186 /** The ID register. */
187 uint8_t volatile u8Id;
188 /** The index register. */
189 uint8_t volatile u8Index;
190 /** Number of CPUs. */
191 uint8_t cCpus;
192 /** I/O APIC version. */
193 uint8_t u8ApicVer;
194 /** I/O APIC ID mask. */
195 uint8_t u8IdMask;
196 /** Maximum Redirection Table Entry (RTE) Entry. */
197 uint8_t u8MaxRte;
198 /** Last valid RTE indirect register index. */
199 uint8_t u8LastRteRegIdx;
200 /* Alignment padding. */
201 uint8_t u8Padding0[1];
202 /** Redirection table entry - Valid write mask. */
203 uint64_t u64RteWriteMask;
204 /** Redirection table entry - Valid read mask. */
205 uint64_t u64RteReadMask;
206
207 /** The redirection table registers. */
208 uint64_t au64RedirTable[IOAPIC_NUM_INTR_PINS];
209 /** The IRQ tags and source IDs for each pin (tracing purposes). */
210 uint32_t au32TagSrc[IOAPIC_NUM_INTR_PINS];
211
212 /** The internal IRR reflecting state of the interrupt lines. */
213 uint32_t uIrr;
214 /** Alignment padding. */
215 uint32_t u32Padding2;
216
217#ifndef IOAPIC_WITH_PDM_CRITSECT
218 /** The critsect for updating to the RTEs. */
219 PDMCRITSECT CritSect;
220#endif
221
222 /** The MMIO region. */
223 IOMMMIOHANDLE hMmio;
224
225#ifdef VBOX_WITH_STATISTICS
226 /** Number of MMIO reads in RZ. */
227 STAMCOUNTER StatMmioReadRZ;
228 /** Number of MMIO reads in R3. */
229 STAMCOUNTER StatMmioReadR3;
230
231 /** Number of MMIO writes in RZ. */
232 STAMCOUNTER StatMmioWriteRZ;
233 /** Number of MMIO writes in R3. */
234 STAMCOUNTER StatMmioWriteR3;
235
236 /** Number of SetIrq calls in RZ. */
237 STAMCOUNTER StatSetIrqRZ;
238 /** Number of SetIrq calls in R3. */
239 STAMCOUNTER StatSetIrqR3;
240
241 /** Number of SetEoi calls in RZ. */
242 STAMCOUNTER StatSetEoiRZ;
243 /** Number of SetEoi calls in R3. */
244 STAMCOUNTER StatSetEoiR3;
245
246 /** Number of redundant edge-triggered interrupts. */
247 STAMCOUNTER StatRedundantEdgeIntr;
248 /** Number of redundant level-triggered interrupts. */
249 STAMCOUNTER StatRedundantLevelIntr;
250 /** Number of suppressed level-triggered interrupts (by remote IRR). */
251 STAMCOUNTER StatSuppressedLevelIntr;
252 /** Number of returns to ring-3 due to EOI broadcast lock contention. */
253 STAMCOUNTER StatEoiContention;
254 /** Number of returns to ring-3 due to Set RTE lock contention. */
255 STAMCOUNTER StatSetRteContention;
256 /** Number of level-triggered interrupts dispatched to the local APIC(s). */
257 STAMCOUNTER StatLevelIrqSent;
258 /** Number of EOIs received for level-triggered interrupts from the local
259 * APIC(s). */
260 STAMCOUNTER StatEoiReceived;
261#endif
262} IOAPIC;
263AssertCompileMemberAlignment(IOAPIC, au64RedirTable, 8);
264/** Pointer to shared IOAPIC data. */
265typedef IOAPIC *PIOAPIC;
266/** Pointer to const shared IOAPIC data. */
267typedef IOAPIC const *PCIOAPIC;
268
269
270/**
271 * The I/O APIC device state for ring-3.
272 */
273typedef struct IOAPICR3
274{
275 /** The IOAPIC helpers. */
276 R3PTRTYPE(PCPDMIOAPICHLP) pIoApicHlp;
277} IOAPICR3;
278/** Pointer to the I/O APIC device state for ring-3. */
279typedef IOAPICR3 *PIOAPICR3;
280
281
282/**
283 * The I/O APIC device state for ring-0.
284 */
285typedef struct IOAPICR0
286{
287 /** The IOAPIC helpers. */
288 R0PTRTYPE(PCPDMIOAPICHLP) pIoApicHlp;
289} IOAPICR0;
290/** Pointer to the I/O APIC device state for ring-0. */
291typedef IOAPICR0 *PIOAPICR0;
292
293
294/**
295 * The I/O APIC device state for raw-mode.
296 */
297typedef struct IOAPICRC
298{
299 /** The IOAPIC helpers. */
300 RCPTRTYPE(PCPDMIOAPICHLP) pIoApicHlp;
301} IOAPICRC;
302/** Pointer to the I/O APIC device state for raw-mode. */
303typedef IOAPICRC *PIOAPICRC;
304
305
306/** The I/O APIC device state for the current context. */
307typedef CTX_SUFF(IOAPIC) IOAPICCC;
308/** Pointer to the I/O APIC device state for the current context. */
309typedef CTX_SUFF(PIOAPIC) PIOAPICCC;
310
311
312#ifndef VBOX_DEVICE_STRUCT_TESTCASE
313
314/**
315 * Gets the arbitration register.
316 *
317 * @returns The arbitration.
318 */
319DECLINLINE(uint32_t) ioapicGetArb(void)
320{
321 Log2(("IOAPIC: ioapicGetArb: returns 0\n"));
322 return 0;
323}
324
325
326/**
327 * Gets the version register.
328 *
329 * @returns The version.
330 */
331DECLINLINE(uint32_t) ioapicGetVersion(PCIOAPIC pThis)
332{
333 uint32_t uValue = RT_MAKE_U32(pThis->u8ApicVer, pThis->u8MaxRte);
334 Log2(("IOAPIC: ioapicGetVersion: returns %#RX32\n", uValue));
335 return uValue;
336}
337
338
339/**
340 * Sets the ID register.
341 *
342 * @param pThis The shared I/O APIC device state.
343 * @param uValue The value to set.
344 */
345DECLINLINE(void) ioapicSetId(PIOAPIC pThis, uint32_t uValue)
346{
347 Log2(("IOAPIC: ioapicSetId: uValue=%#RX32\n", uValue));
348 ASMAtomicWriteU8(&pThis->u8Id, (uValue >> 24) & pThis->u8IdMask);
349}
350
351
352/**
353 * Gets the ID register.
354 *
355 * @returns The ID.
356 * @param pThis The shared I/O APIC device state.
357 */
358DECLINLINE(uint32_t) ioapicGetId(PCIOAPIC pThis)
359{
360 uint32_t uValue = (uint32_t)pThis->u8Id << 24;
361 Log2(("IOAPIC: ioapicGetId: returns %#RX32\n", uValue));
362 return uValue;
363}
364
365
366/**
367 * Sets the index register.
368 *
369 * @param pThis The shared I/O APIC device state.
370 * @param uValue The value to set.
371 */
372DECLINLINE(void) ioapicSetIndex(PIOAPIC pThis, uint32_t uValue)
373{
374 LogFlow(("IOAPIC: ioapicSetIndex: uValue=%#RX32\n", uValue));
375 ASMAtomicWriteU8(&pThis->u8Index, uValue & IOAPIC_INDEX_VALID_WRITE_MASK);
376}
377
378
379/**
380 * Gets the index register.
381 *
382 * @returns The index value.
383 */
384DECLINLINE(uint32_t) ioapicGetIndex(PCIOAPIC pThis)
385{
386 uint32_t const uValue = pThis->u8Index;
387 LogFlow(("IOAPIC: ioapicGetIndex: returns %#x\n", uValue));
388 return uValue;
389}
390
391
392/**
393 * Signals the next pending interrupt for the specified Redirection Table Entry
394 * (RTE).
395 *
396 * @param pDevIns The device instance.
397 * @param pThis The shared I/O APIC device state.
398 * @param pThisCC The I/O APIC device state for the current context.
399 * @param idxRte The index of the RTE (validated).
400 *
401 * @remarks It is the responsibility of the caller to verify that an interrupt is
402 * pending for the pin corresponding to the RTE before calling this
403 * function.
404 */
405static void ioapicSignalIntrForRte(PPDMDEVINS pDevIns, PIOAPIC pThis, PIOAPICCC pThisCC, uint8_t idxRte)
406{
407#ifndef IOAPIC_WITH_PDM_CRITSECT
408 Assert(PDMCritSectIsOwner(&pThis->CritSect));
409#endif
410
411 /* Ensure the RTE isn't masked. */
412 uint64_t const u64Rte = pThis->au64RedirTable[idxRte];
413 if (!IOAPIC_RTE_IS_MASKED(u64Rte))
414 {
415 /* We cannot accept another level-triggered interrupt until remote IRR has been cleared. */
416 uint8_t const u8TriggerMode = IOAPIC_RTE_GET_TRIGGER_MODE(u64Rte);
417 if (u8TriggerMode == IOAPIC_RTE_TRIGGER_MODE_LEVEL)
418 {
419 uint8_t const u8RemoteIrr = IOAPIC_RTE_GET_REMOTE_IRR(u64Rte);
420 if (u8RemoteIrr)
421 {
422 STAM_COUNTER_INC(&pThis->StatSuppressedLevelIntr);
423 return;
424 }
425 }
426
427 uint8_t const u8Vector = IOAPIC_RTE_GET_VECTOR(u64Rte);
428 uint8_t const u8DeliveryMode = IOAPIC_RTE_GET_DELIVERY_MODE(u64Rte);
429 uint8_t const u8DestMode = IOAPIC_RTE_GET_DEST_MODE(u64Rte);
430 uint8_t const u8Polarity = IOAPIC_RTE_GET_POLARITY(u64Rte);
431 uint8_t const u8Dest = IOAPIC_RTE_GET_DEST(u64Rte);
432 uint32_t const u32TagSrc = pThis->au32TagSrc[idxRte];
433
434 Log2(("IOAPIC: Signaling %s-triggered interrupt. Dest=%#x DestMode=%s Vector=%#x (%u)\n",
435 u8TriggerMode == IOAPIC_RTE_TRIGGER_MODE_EDGE ? "edge" : "level", u8Dest,
436 u8DestMode == IOAPIC_RTE_DEST_MODE_PHYSICAL ? "physical" : "logical", u8Vector, u8Vector));
437
438 /*
439 * Deliver to the local APIC via the system/3-wire-APIC bus.
440 */
441 int rc = pThisCC->pIoApicHlp->pfnApicBusDeliver(pDevIns,
442 u8Dest,
443 u8DestMode,
444 u8DeliveryMode,
445 u8Vector,
446 u8Polarity,
447 u8TriggerMode,
448 u32TagSrc);
449 /* Can't reschedule to R3. */
450 Assert(rc == VINF_SUCCESS || rc == VERR_APIC_INTR_DISCARDED);
451#ifdef DEBUG_ramshankar
452 if (rc == VERR_APIC_INTR_DISCARDED)
453 AssertMsgFailed(("APIC: Interrupt discarded u8Vector=%#x (%u) u64Rte=%#RX64\n", u8Vector, u8Vector, u64Rte));
454#endif
455
456 /*
457 * For level-triggered interrupts, we set the remote IRR bit to indicate
458 * the local APIC has accepted the interrupt.
459 *
460 * For edge-triggered interrupts, we should not clear the IRR bit as it
461 * should remain intact to reflect the state of the interrupt line.
462 * The device will explicitly transition to inactive state via the
463 * ioapicSetIrq() callback.
464 */
465 if ( u8TriggerMode == IOAPIC_RTE_TRIGGER_MODE_LEVEL
466 && rc == VINF_SUCCESS)
467 {
468 Assert(u8TriggerMode == IOAPIC_RTE_TRIGGER_MODE_LEVEL);
469 pThis->au64RedirTable[idxRte] |= IOAPIC_RTE_REMOTE_IRR;
470 STAM_COUNTER_INC(&pThis->StatLevelIrqSent);
471 }
472 }
473}
474
475
476/**
477 * Gets the redirection table entry.
478 *
479 * @returns The redirection table entry.
480 * @param pThis The shared I/O APIC device state.
481 * @param uIndex The index value.
482 */
483DECLINLINE(uint32_t) ioapicGetRedirTableEntry(PCIOAPIC pThis, uint32_t uIndex)
484{
485 uint8_t const idxRte = (uIndex - IOAPIC_INDIRECT_INDEX_REDIR_TBL_START) >> 1;
486 AssertMsgReturn(idxRte < RT_ELEMENTS(pThis->au64RedirTable),
487 ("Invalid index %u, expected < %u\n", idxRte, RT_ELEMENTS(pThis->au64RedirTable)),
488 UINT32_MAX);
489 uint32_t uValue;
490 if (!(uIndex & 1))
491 uValue = RT_LO_U32(pThis->au64RedirTable[idxRte]) & RT_LO_U32(pThis->u64RteReadMask);
492 else
493 uValue = RT_HI_U32(pThis->au64RedirTable[idxRte]) & RT_HI_U32(pThis->u64RteReadMask);
494
495 LogFlow(("IOAPIC: ioapicGetRedirTableEntry: uIndex=%#RX32 idxRte=%u returns %#RX32\n", uIndex, idxRte, uValue));
496 return uValue;
497}
498
499
500/**
501 * Sets the redirection table entry.
502 *
503 * @returns Strict VBox status code (VINF_IOM_R3_MMIO_WRITE / VINF_SUCCESS).
504 * @param pDevIns The device instance.
505 * @param pThis The shared I/O APIC device state.
506 * @param pThisCC The I/O APIC device state for the current context.
507 * @param uIndex The index value.
508 * @param uValue The value to set.
509 */
510static VBOXSTRICTRC ioapicSetRedirTableEntry(PPDMDEVINS pDevIns, PIOAPIC pThis, PIOAPICCC pThisCC,
511 uint32_t uIndex, uint32_t uValue)
512{
513 uint8_t const idxRte = (uIndex - IOAPIC_INDIRECT_INDEX_REDIR_TBL_START) >> 1;
514 AssertMsgReturn(idxRte < RT_ELEMENTS(pThis->au64RedirTable),
515 ("Invalid index %u, expected < %u\n", idxRte, RT_ELEMENTS(pThis->au64RedirTable)),
516 VINF_SUCCESS);
517
518 VBOXSTRICTRC rc = IOAPIC_LOCK(pDevIns, pThis, pThisCC, VINF_IOM_R3_MMIO_WRITE);
519 if (rc == VINF_SUCCESS)
520 {
521 /*
522 * Write the low or high 32-bit value into the specified 64-bit RTE register,
523 * update only the valid, writable bits.
524 *
525 * We need to preserve the read-only bits as it can have dire consequences
526 * otherwise, see @bugref{8386#c24}.
527 */
528 uint64_t const u64Rte = pThis->au64RedirTable[idxRte];
529 if (!(uIndex & 1))
530 {
531 uint32_t const u32RtePreserveLo = RT_LO_U32(u64Rte) & ~RT_LO_U32(pThis->u64RteWriteMask);
532 uint32_t const u32RteNewLo = (uValue & RT_LO_U32(pThis->u64RteWriteMask)) | u32RtePreserveLo;
533 uint64_t const u64RteHi = u64Rte & UINT64_C(0xffffffff00000000);
534 pThis->au64RedirTable[idxRte] = u64RteHi | u32RteNewLo;
535 }
536 else
537 {
538 uint32_t const u32RtePreserveHi = RT_HI_U32(u64Rte) & ~RT_HI_U32(pThis->u64RteWriteMask);
539 uint32_t const u32RteLo = RT_LO_U32(u64Rte);
540 uint64_t const u64RteNewHi = ((uint64_t)((uValue & RT_HI_U32(pThis->u64RteWriteMask)) | u32RtePreserveHi) << 32);
541 pThis->au64RedirTable[idxRte] = u64RteNewHi | u32RteLo;
542 }
543
544 /*
545 * Signal the next pending interrupt for this RTE.
546 */
547 uint32_t const uPinMask = UINT32_C(1) << idxRte;
548 if (pThis->uIrr & uPinMask)
549 ioapicSignalIntrForRte(pDevIns, pThis, pThisCC, idxRte);
550
551 IOAPIC_UNLOCK(pDevIns, pThis, pThisCC);
552 LogFlow(("IOAPIC: ioapicSetRedirTableEntry: uIndex=%#RX32 idxRte=%u uValue=%#RX32\n", uIndex, idxRte, uValue));
553 }
554 else
555 STAM_COUNTER_INC(&pThis->StatSetRteContention);
556
557 return rc;
558}
559
560
561/**
562 * Gets the data register.
563 *
564 * @returns The data value.
565 * @param pThis The shared I/O APIC device state.
566 */
567static uint32_t ioapicGetData(PCIOAPIC pThis)
568{
569 uint8_t const uIndex = pThis->u8Index;
570 RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
571 if ( uIndex >= IOAPIC_INDIRECT_INDEX_REDIR_TBL_START
572 && uIndex <= pThis->u8LastRteRegIdx)
573 return ioapicGetRedirTableEntry(pThis, uIndex);
574
575 uint32_t uValue;
576 switch (uIndex)
577 {
578 case IOAPIC_INDIRECT_INDEX_ID:
579 uValue = ioapicGetId(pThis);
580 break;
581
582 case IOAPIC_INDIRECT_INDEX_VERSION:
583 uValue = ioapicGetVersion(pThis);
584 break;
585
586 case IOAPIC_INDIRECT_INDEX_ARB:
587 if (pThis->u8ApicVer == IOAPIC_VERSION_82093AA)
588 {
589 uValue = ioapicGetArb();
590 break;
591 }
592 RT_FALL_THRU();
593
594 default:
595 uValue = UINT32_C(0xffffffff);
596 Log2(("IOAPIC: Attempt to read register at invalid index %#x\n", uIndex));
597 break;
598 }
599 return uValue;
600}
601
602
603/**
604 * Sets the data register.
605 *
606 * @returns Strict VBox status code.
607 * @param pDevIns The device instance.
608 * @param pThis The shared I/O APIC device state.
609 * @param pThisCC The I/O APIC device state for the current context.
610 * @param uValue The value to set.
611 */
612static VBOXSTRICTRC ioapicSetData(PPDMDEVINS pDevIns, PIOAPIC pThis, PIOAPICCC pThisCC, uint32_t uValue)
613{
614 uint8_t const uIndex = pThis->u8Index;
615 RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
616 LogFlow(("IOAPIC: ioapicSetData: uIndex=%#x uValue=%#RX32\n", uIndex, uValue));
617
618 if ( uIndex >= IOAPIC_INDIRECT_INDEX_REDIR_TBL_START
619 && uIndex <= pThis->u8LastRteRegIdx)
620 return ioapicSetRedirTableEntry(pDevIns, pThis, pThisCC, uIndex, uValue);
621
622 if (uIndex == IOAPIC_INDIRECT_INDEX_ID)
623 ioapicSetId(pThis, uValue);
624 else
625 Log2(("IOAPIC: ioapicSetData: Invalid index %#RX32, ignoring write request with uValue=%#RX32\n", uIndex, uValue));
626
627 return VINF_SUCCESS;
628}
629
630
631/**
632 * @interface_method_impl{PDMIOAPICREG,pfnSetEoi}
633 */
634static DECLCALLBACK(VBOXSTRICTRC) ioapicSetEoi(PPDMDEVINS pDevIns, uint8_t u8Vector)
635{
636 PIOAPIC pThis = PDMDEVINS_2_DATA(pDevIns, PIOAPIC);
637 PIOAPICCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PIOAPICCC);
638 STAM_COUNTER_INC(&pThis->CTX_SUFF_Z(StatSetEoi));
639 LogFlow(("IOAPIC: ioapicSetEoi: u8Vector=%#x (%u)\n", u8Vector, u8Vector));
640
641 bool fRemoteIrrCleared = false;
642 VBOXSTRICTRC rc = IOAPIC_LOCK(pDevIns, pThis, pThisCC, VINF_IOM_R3_MMIO_WRITE);
643 if (rc == VINF_SUCCESS)
644 {
645 for (uint8_t idxRte = 0; idxRte < RT_ELEMENTS(pThis->au64RedirTable); idxRte++)
646 {
647 uint64_t const u64Rte = pThis->au64RedirTable[idxRte];
648 if (IOAPIC_RTE_GET_VECTOR(u64Rte) == u8Vector)
649 {
650#ifdef DEBUG_ramshankar
651 /* This assertion may trigger when restoring saved-states created using the old, incorrect I/O APIC code. */
652 Assert(IOAPIC_RTE_GET_REMOTE_IRR(u64Rte));
653#endif
654 pThis->au64RedirTable[idxRte] &= ~IOAPIC_RTE_REMOTE_IRR;
655 fRemoteIrrCleared = true;
656 STAM_COUNTER_INC(&pThis->StatEoiReceived);
657 Log2(("IOAPIC: ioapicSetEoi: Cleared remote IRR, idxRte=%u vector=%#x (%u)\n", idxRte, u8Vector, u8Vector));
658
659 /*
660 * Signal the next pending interrupt for this RTE.
661 */
662 uint32_t const uPinMask = UINT32_C(1) << idxRte;
663 if (pThis->uIrr & uPinMask)
664 ioapicSignalIntrForRte(pDevIns, pThis, pThisCC, idxRte);
665 }
666 }
667
668 IOAPIC_UNLOCK(pDevIns, pThis, pThisCC);
669 AssertMsg(fRemoteIrrCleared, ("Failed to clear remote IRR for vector %#x (%u)\n", u8Vector, u8Vector));
670 }
671 else
672 STAM_COUNTER_INC(&pThis->StatEoiContention);
673
674 return rc;
675}
676
677
678/**
679 * @interface_method_impl{PDMIOAPICREG,pfnSetIrq}
680 */
681static DECLCALLBACK(void) ioapicSetIrq(PPDMDEVINS pDevIns, int iIrq, int iLevel, uint32_t uTagSrc)
682{
683#define IOAPIC_ASSERT_IRQ(a_idxRte, a_PinMask) do { \
684 pThis->au32TagSrc[(a_idxRte)] = !pThis->au32TagSrc[(a_idxRte)] ? uTagSrc : RT_BIT_32(31); \
685 pThis->uIrr |= a_PinMask; \
686 ioapicSignalIntrForRte(pDevIns, pThis, pThisCC, (a_idxRte)); \
687 } while (0)
688
689 PIOAPIC pThis = PDMDEVINS_2_DATA(pDevIns, PIOAPIC);
690 PIOAPICCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PIOAPICCC);
691 LogFlow(("IOAPIC: ioapicSetIrq: iIrq=%d iLevel=%d uTagSrc=%#x\n", iIrq, iLevel, uTagSrc));
692
693 STAM_COUNTER_INC(&pThis->CTX_SUFF_Z(StatSetIrq));
694
695 if (RT_LIKELY((unsigned)iIrq < RT_ELEMENTS(pThis->au64RedirTable)))
696 {
697 int rc = IOAPIC_LOCK(pDevIns, pThis, pThisCC, VINF_SUCCESS);
698 AssertRC(rc);
699
700 uint8_t const idxRte = iIrq;
701 uint32_t const uPinMask = UINT32_C(1) << idxRte;
702 uint32_t const u32RteLo = RT_LO_U32(pThis->au64RedirTable[idxRte]);
703 uint8_t const u8TriggerMode = IOAPIC_RTE_GET_TRIGGER_MODE(u32RteLo);
704
705 bool fActive = RT_BOOL(iLevel & 1);
706 /** @todo Polarity is busted elsewhere, we need to fix that
707 * first. See @bugref{8386#c7}. */
708#if 0
709 uint8_t const u8Polarity = IOAPIC_RTE_GET_POLARITY(u32RteLo);
710 fActive ^= u8Polarity; */
711#endif
712 if (!fActive)
713 {
714 pThis->uIrr &= ~uPinMask;
715 IOAPIC_UNLOCK(pDevIns, pThis, pThisCC);
716 return;
717 }
718
719 bool const fFlipFlop = ((iLevel & PDM_IRQ_LEVEL_FLIP_FLOP) == PDM_IRQ_LEVEL_FLIP_FLOP);
720 uint32_t const uPrevIrr = pThis->uIrr & uPinMask;
721 if (!fFlipFlop)
722 {
723 if (u8TriggerMode == IOAPIC_RTE_TRIGGER_MODE_EDGE)
724 {
725 /*
726 * For edge-triggered interrupts, we need to act only on a low to high edge transition.
727 * See ICH9 spec. 13.5.7 "REDIR_TBL: Redirection Table (LPC I/F-D31:F0)".
728 */
729 if (!uPrevIrr)
730 IOAPIC_ASSERT_IRQ(idxRte, uPinMask);
731 else
732 {
733 STAM_COUNTER_INC(&pThis->StatRedundantEdgeIntr);
734 Log2(("IOAPIC: Redundant edge-triggered interrupt %#x (%u)\n", idxRte, idxRte));
735 }
736 }
737 else
738 {
739 Assert(u8TriggerMode == IOAPIC_RTE_TRIGGER_MODE_LEVEL);
740
741 /*
742 * For level-triggered interrupts, redundant interrupts are not a problem
743 * and will eventually be delivered anyway after an EOI, but our PDM devices
744 * should not typically call us with no change to the level.
745 */
746 if (!uPrevIrr)
747 { /* likely */ }
748 else
749 {
750 STAM_COUNTER_INC(&pThis->StatRedundantLevelIntr);
751 Log2(("IOAPIC: Redundant level-triggered interrupt %#x (%u)\n", idxRte, idxRte));
752 }
753
754 IOAPIC_ASSERT_IRQ(idxRte, uPinMask);
755 }
756 }
757 else
758 {
759 /*
760 * The device is flip-flopping the interrupt line, which implies we should de-assert
761 * and assert the interrupt line. The interrupt line is left in the asserted state
762 * after a flip-flop request. The de-assert is a NOP wrts to signaling an interrupt
763 * hence just the assert is done.
764 */
765 IOAPIC_ASSERT_IRQ(idxRte, uPinMask);
766 }
767
768 IOAPIC_UNLOCK(pDevIns, pThis, pThisCC);
769 }
770#undef IOAPIC_ASSERT_IRQ
771}
772
773
774/**
775 * @interface_method_impl{PDMIOAPICREG,pfnSendMsi}
776 */
777static DECLCALLBACK(void) ioapicSendMsi(PPDMDEVINS pDevIns, RTGCPHYS GCPhys, uint32_t uValue, uint32_t uTagSrc)
778{
779 PIOAPICCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PIOAPICCC);
780 LogFlow(("IOAPIC: ioapicSendMsi: GCPhys=%#RGp uValue=%#RX32\n", GCPhys, uValue));
781
782 /*
783 * Parse the message from the physical address.
784 * See Intel spec. 10.11.1 "Message Address Register Format".
785 */
786 uint8_t const u8DestAddr = (GCPhys & VBOX_MSI_ADDR_DEST_ID_MASK) >> VBOX_MSI_ADDR_DEST_ID_SHIFT;
787 uint8_t const u8DestMode = (GCPhys >> VBOX_MSI_ADDR_DEST_MODE_SHIFT) & 0x1;
788 /** @todo Check if we need to implement Redirection Hint Indicator. */
789 /* uint8_t const uRedirectHint = (GCPhys >> VBOX_MSI_ADDR_REDIRECTION_SHIFT) & 0x1; */
790
791 /*
792 * Parse the message data.
793 * See Intel spec. 10.11.2 "Message Data Register Format".
794 */
795 uint8_t const u8Vector = (uValue & VBOX_MSI_DATA_VECTOR_MASK) >> VBOX_MSI_DATA_VECTOR_SHIFT;
796 uint8_t const u8TriggerMode = (uValue >> VBOX_MSI_DATA_TRIGGER_SHIFT) & 0x1;
797 uint8_t const u8DeliveryMode = (uValue >> VBOX_MSI_DATA_DELIVERY_MODE_SHIFT) & 0x7;
798
799 /*
800 * Deliver to the local APIC via the system/3-wire-APIC bus.
801 */
802 int rc = pThisCC->pIoApicHlp->pfnApicBusDeliver(pDevIns,
803 u8DestAddr,
804 u8DestMode,
805 u8DeliveryMode,
806 u8Vector,
807 0 /* u8Polarity - N/A */,
808 u8TriggerMode,
809 uTagSrc);
810 /* Can't reschedule to R3. */
811 Assert(rc == VINF_SUCCESS || rc == VERR_APIC_INTR_DISCARDED); NOREF(rc);
812}
813
814
815/**
816 * @callback_method_impl{FNIOMMMIONEWREAD}
817 */
818static DECLCALLBACK(VBOXSTRICTRC) ioapicMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
819{
820 PIOAPIC pThis = PDMDEVINS_2_DATA(pDevIns, PIOAPIC);
821 STAM_COUNTER_INC(&pThis->CTX_SUFF_Z(StatMmioRead));
822 Assert(cb == 4); RT_NOREF_PV(cb); /* registered for dwords only */
823 RT_NOREF_PV(pvUser);
824
825 VBOXSTRICTRC rc = VINF_SUCCESS;
826 uint32_t *puValue = (uint32_t *)pv;
827 uint32_t offReg = off & IOAPIC_MMIO_REG_MASK;
828 switch (offReg)
829 {
830 case IOAPIC_DIRECT_OFF_INDEX:
831 *puValue = ioapicGetIndex(pThis);
832 break;
833
834 case IOAPIC_DIRECT_OFF_DATA:
835 *puValue = ioapicGetData(pThis);
836 break;
837
838 default:
839 Log2(("IOAPIC: ioapicMmioRead: Invalid offset. off=%#RGp offReg=%#x\n", off, offReg));
840 rc = VINF_IOM_MMIO_UNUSED_FF;
841 break;
842 }
843
844 LogFlow(("IOAPIC: ioapicMmioRead: offReg=%#x, returns %#RX32\n", offReg, *puValue));
845 return rc;
846}
847
848
849/**
850 * @callback_method_impl{FNIOMMMIONEWWRITE}
851 */
852static DECLCALLBACK(VBOXSTRICTRC) ioapicMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
853{
854 PIOAPIC pThis = PDMDEVINS_2_DATA(pDevIns, PIOAPIC);
855 PIOAPICCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PIOAPICCC);
856 RT_NOREF_PV(pvUser);
857
858 STAM_COUNTER_INC(&pThis->CTX_SUFF_Z(StatMmioWrite));
859
860 Assert(!(off & 3));
861 Assert(cb == 4); RT_NOREF_PV(cb); /* registered for dwords only */
862
863 VBOXSTRICTRC rc = VINF_SUCCESS;
864 uint32_t const uValue = *(uint32_t const *)pv;
865 uint32_t const offReg = off & IOAPIC_MMIO_REG_MASK;
866
867 LogFlow(("IOAPIC: ioapicMmioWrite: pThis=%p off=%#RGp cb=%u uValue=%#RX32\n", pThis, off, cb, uValue));
868 switch (offReg)
869 {
870 case IOAPIC_DIRECT_OFF_INDEX:
871 ioapicSetIndex(pThis, uValue);
872 break;
873
874 case IOAPIC_DIRECT_OFF_DATA:
875 rc = ioapicSetData(pDevIns, pThis, pThisCC, uValue);
876 break;
877
878 case IOAPIC_DIRECT_OFF_EOI:
879 if (pThis->u8ApicVer == IOAPIC_VERSION_ICH9)
880 rc = ioapicSetEoi(pDevIns, uValue);
881 else
882 Log(("IOAPIC: ioapicMmioWrite: Write to EOI register ignored!\n"));
883 break;
884
885 default:
886 Log2(("IOAPIC: ioapicMmioWrite: Invalid offset. off=%#RGp offReg=%#x\n", off, offReg));
887 break;
888 }
889
890 return rc;
891}
892
893
894#ifdef IN_RING3
895
896/** @interface_method_impl{DBGFREGDESC,pfnGet} */
897static DECLCALLBACK(int) ioapicR3DbgReg_GetIndex(void *pvUser, PCDBGFREGDESC pDesc, PDBGFREGVAL pValue)
898{
899 RT_NOREF(pDesc);
900 pValue->u32 = ioapicGetIndex(PDMDEVINS_2_DATA((PPDMDEVINS)pvUser, PCIOAPIC));
901 return VINF_SUCCESS;
902}
903
904
905/** @interface_method_impl{DBGFREGDESC,pfnSet} */
906static DECLCALLBACK(int) ioapicR3DbgReg_SetIndex(void *pvUser, PCDBGFREGDESC pDesc, PCDBGFREGVAL pValue, PCDBGFREGVAL pfMask)
907{
908 RT_NOREF(pDesc, pfMask);
909 ioapicSetIndex(PDMDEVINS_2_DATA((PPDMDEVINS)pvUser, PIOAPIC), pValue->u8);
910 return VINF_SUCCESS;
911}
912
913
914/** @interface_method_impl{DBGFREGDESC,pfnGet} */
915static DECLCALLBACK(int) ioapicR3DbgReg_GetData(void *pvUser, PCDBGFREGDESC pDesc, PDBGFREGVAL pValue)
916{
917 RT_NOREF(pDesc);
918 pValue->u32 = ioapicGetData((PDMDEVINS_2_DATA((PPDMDEVINS)pvUser, PCIOAPIC)));
919 return VINF_SUCCESS;
920}
921
922
923/** @interface_method_impl{DBGFREGDESC,pfnSet} */
924static DECLCALLBACK(int) ioapicR3DbgReg_SetData(void *pvUser, PCDBGFREGDESC pDesc, PCDBGFREGVAL pValue, PCDBGFREGVAL pfMask)
925{
926 PPDMDEVINS pDevIns = (PPDMDEVINS)pvUser;
927 PIOAPIC pThis = PDMDEVINS_2_DATA(pDevIns, PIOAPIC);
928 PIOAPICCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PIOAPICCC);
929 RT_NOREF(pDesc, pfMask);
930 return VBOXSTRICTRC_VAL(ioapicSetData(pDevIns, pThis, pThisCC, pValue->u32));
931}
932
933
934/** @interface_method_impl{DBGFREGDESC,pfnGet} */
935static DECLCALLBACK(int) ioapicR3DbgReg_GetVersion(void *pvUser, PCDBGFREGDESC pDesc, PDBGFREGVAL pValue)
936{
937 PCIOAPIC pThis = PDMDEVINS_2_DATA((PPDMDEVINS)pvUser, PCIOAPIC);
938 RT_NOREF(pDesc);
939 pValue->u32 = ioapicGetVersion(pThis);
940 return VINF_SUCCESS;
941}
942
943
944/** @interface_method_impl{DBGFREGDESC,pfnGet} */
945static DECLCALLBACK(int) ioapicR3DbgReg_GetArb(void *pvUser, PCDBGFREGDESC pDesc, PDBGFREGVAL pValue)
946{
947 RT_NOREF(pvUser, pDesc);
948 pValue->u32 = ioapicGetArb();
949 return VINF_SUCCESS;
950}
951
952
953/** @interface_method_impl{DBGFREGDESC,pfnGet} */
954static DECLCALLBACK(int) ioapicR3DbgReg_GetRte(void *pvUser, PCDBGFREGDESC pDesc, PDBGFREGVAL pValue)
955{
956 PCIOAPIC pThis = PDMDEVINS_2_DATA((PPDMDEVINS)pvUser, PCIOAPIC);
957 Assert(pDesc->offRegister < RT_ELEMENTS(pThis->au64RedirTable));
958 pValue->u64 = pThis->au64RedirTable[pDesc->offRegister];
959 return VINF_SUCCESS;
960}
961
962
963/** @interface_method_impl{DBGFREGDESC,pfnSet} */
964static DECLCALLBACK(int) ioapicR3DbgReg_SetRte(void *pvUser, PCDBGFREGDESC pDesc, PCDBGFREGVAL pValue, PCDBGFREGVAL pfMask)
965{
966 RT_NOREF(pfMask);
967 PIOAPIC pThis = PDMDEVINS_2_DATA((PPDMDEVINS)pvUser, PIOAPIC);
968 /* No locks, no checks, just do it. */
969 Assert(pDesc->offRegister < RT_ELEMENTS(pThis->au64RedirTable));
970 pThis->au64RedirTable[pDesc->offRegister] = pValue->u64;
971 return VINF_SUCCESS;
972}
973
974
975/** IOREDTBLn sub fields. */
976static DBGFREGSUBFIELD const g_aRteSubs[] =
977{
978 { "vector", 0, 8, 0, 0, NULL, NULL },
979 { "dlvr_mode", 8, 3, 0, 0, NULL, NULL },
980 { "dest_mode", 11, 1, 0, 0, NULL, NULL },
981 { "dlvr_status", 12, 1, 0, DBGFREGSUBFIELD_FLAGS_READ_ONLY, NULL, NULL },
982 { "polarity", 13, 1, 0, 0, NULL, NULL },
983 { "remote_irr", 14, 1, 0, DBGFREGSUBFIELD_FLAGS_READ_ONLY, NULL, NULL },
984 { "trigger_mode", 15, 1, 0, 0, NULL, NULL },
985 { "mask", 16, 1, 0, 0, NULL, NULL },
986 { "ext_dest_id", 48, 8, 0, DBGFREGSUBFIELD_FLAGS_READ_ONLY, NULL, NULL },
987 { "dest", 56, 8, 0, 0, NULL, NULL },
988 DBGFREGSUBFIELD_TERMINATOR()
989};
990
991
992/** Register descriptors for DBGF. */
993static DBGFREGDESC const g_aRegDesc[] =
994{
995 { "index", DBGFREG_END, DBGFREGVALTYPE_U8, 0, 0, ioapicR3DbgReg_GetIndex, ioapicR3DbgReg_SetIndex, NULL, NULL },
996 { "data", DBGFREG_END, DBGFREGVALTYPE_U32, 0, 0, ioapicR3DbgReg_GetData, ioapicR3DbgReg_SetData, NULL, NULL },
997 { "version", DBGFREG_END, DBGFREGVALTYPE_U32, DBGFREG_FLAGS_READ_ONLY, 0, ioapicR3DbgReg_GetVersion, NULL, NULL, NULL },
998 { "arb", DBGFREG_END, DBGFREGVALTYPE_U32, DBGFREG_FLAGS_READ_ONLY, 0, ioapicR3DbgReg_GetArb, NULL, NULL, NULL },
999 { "rte0", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 0, ioapicR3DbgReg_GetRte, ioapicR3DbgReg_SetRte, NULL, &g_aRteSubs[0] },
1000 { "rte1", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 1, ioapicR3DbgReg_GetRte, ioapicR3DbgReg_SetRte, NULL, &g_aRteSubs[0] },
1001 { "rte2", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 2, ioapicR3DbgReg_GetRte, ioapicR3DbgReg_SetRte, NULL, &g_aRteSubs[0] },
1002 { "rte3", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 3, ioapicR3DbgReg_GetRte, ioapicR3DbgReg_SetRte, NULL, &g_aRteSubs[0] },
1003 { "rte4", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 4, ioapicR3DbgReg_GetRte, ioapicR3DbgReg_SetRte, NULL, &g_aRteSubs[0] },
1004 { "rte5", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 5, ioapicR3DbgReg_GetRte, ioapicR3DbgReg_SetRte, NULL, &g_aRteSubs[0] },
1005 { "rte6", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 6, ioapicR3DbgReg_GetRte, ioapicR3DbgReg_SetRte, NULL, &g_aRteSubs[0] },
1006 { "rte7", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 7, ioapicR3DbgReg_GetRte, ioapicR3DbgReg_SetRte, NULL, &g_aRteSubs[0] },
1007 { "rte8", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 8, ioapicR3DbgReg_GetRte, ioapicR3DbgReg_SetRte, NULL, &g_aRteSubs[0] },
1008 { "rte9", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 9, ioapicR3DbgReg_GetRte, ioapicR3DbgReg_SetRte, NULL, &g_aRteSubs[0] },
1009 { "rte10", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 10, ioapicR3DbgReg_GetRte, ioapicR3DbgReg_SetRte, NULL, &g_aRteSubs[0] },
1010 { "rte11", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 11, ioapicR3DbgReg_GetRte, ioapicR3DbgReg_SetRte, NULL, &g_aRteSubs[0] },
1011 { "rte12", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 12, ioapicR3DbgReg_GetRte, ioapicR3DbgReg_SetRte, NULL, &g_aRteSubs[0] },
1012 { "rte13", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 13, ioapicR3DbgReg_GetRte, ioapicR3DbgReg_SetRte, NULL, &g_aRteSubs[0] },
1013 { "rte14", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 14, ioapicR3DbgReg_GetRte, ioapicR3DbgReg_SetRte, NULL, &g_aRteSubs[0] },
1014 { "rte15", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 15, ioapicR3DbgReg_GetRte, ioapicR3DbgReg_SetRte, NULL, &g_aRteSubs[0] },
1015 { "rte16", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 16, ioapicR3DbgReg_GetRte, ioapicR3DbgReg_SetRte, NULL, &g_aRteSubs[0] },
1016 { "rte17", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 17, ioapicR3DbgReg_GetRte, ioapicR3DbgReg_SetRte, NULL, &g_aRteSubs[0] },
1017 { "rte18", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 18, ioapicR3DbgReg_GetRte, ioapicR3DbgReg_SetRte, NULL, &g_aRteSubs[0] },
1018 { "rte19", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 19, ioapicR3DbgReg_GetRte, ioapicR3DbgReg_SetRte, NULL, &g_aRteSubs[0] },
1019 { "rte20", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 20, ioapicR3DbgReg_GetRte, ioapicR3DbgReg_SetRte, NULL, &g_aRteSubs[0] },
1020 { "rte21", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 21, ioapicR3DbgReg_GetRte, ioapicR3DbgReg_SetRte, NULL, &g_aRteSubs[0] },
1021 { "rte22", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 22, ioapicR3DbgReg_GetRte, ioapicR3DbgReg_SetRte, NULL, &g_aRteSubs[0] },
1022 { "rte23", DBGFREG_END, DBGFREGVALTYPE_U64, 0, 23, ioapicR3DbgReg_GetRte, ioapicR3DbgReg_SetRte, NULL, &g_aRteSubs[0] },
1023 DBGFREGDESC_TERMINATOR()
1024};
1025
1026
1027/**
1028 * @callback_method_impl{FNDBGFHANDLERDEV}
1029 */
1030static DECLCALLBACK(void) ioapicR3DbgInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
1031{
1032 RT_NOREF(pszArgs);
1033 PCIOAPIC pThis = PDMDEVINS_2_DATA(pDevIns, PIOAPIC);
1034 LogFlow(("IOAPIC: ioapicR3DbgInfo: pThis=%p pszArgs=%s\n", pThis, pszArgs));
1035
1036 pHlp->pfnPrintf(pHlp, "I/O APIC at %#010x:\n", IOAPIC_MMIO_BASE_PHYSADDR);
1037
1038 uint32_t const uId = ioapicGetId(pThis);
1039 pHlp->pfnPrintf(pHlp, " ID = %#RX32\n", uId);
1040 pHlp->pfnPrintf(pHlp, " ID = %#x\n", IOAPIC_ID_GET_ID(uId));
1041
1042 uint32_t const uVer = ioapicGetVersion(pThis);
1043 pHlp->pfnPrintf(pHlp, " Version = %#RX32\n", uVer);
1044 pHlp->pfnPrintf(pHlp, " Version = %#x\n", IOAPIC_VER_GET_VER(uVer));
1045 pHlp->pfnPrintf(pHlp, " Pin Assert Reg. Support = %RTbool\n", IOAPIC_VER_HAS_PRQ(uVer));
1046 pHlp->pfnPrintf(pHlp, " Max. Redirection Entry = %u\n", IOAPIC_VER_GET_MRE(uVer));
1047
1048 if (pThis->u8ApicVer == IOAPIC_VERSION_82093AA)
1049 {
1050 uint32_t const uArb = ioapicGetArb();
1051 pHlp->pfnPrintf(pHlp, " Arbitration = %#RX32\n", uArb);
1052 pHlp->pfnPrintf(pHlp, " Arbitration ID = %#x\n", IOAPIC_ARB_GET_ID(uArb));
1053 }
1054
1055 pHlp->pfnPrintf(pHlp, " Current index = %#x\n", ioapicGetIndex(pThis));
1056
1057 pHlp->pfnPrintf(pHlp, " I/O Redirection Table and IRR:\n");
1058 pHlp->pfnPrintf(pHlp, " idx dst_mode dst_addr mask irr trigger rirr polar dlvr_st dlvr_mode vector\n");
1059
1060 uint8_t const idxMaxRte = RT_MIN(pThis->u8MaxRte, RT_ELEMENTS(pThis->au64RedirTable) - 1);
1061 for (uint8_t idxRte = 0; idxRte <= idxMaxRte; idxRte++)
1062 {
1063 static const char * const s_apszDeliveryModes[] =
1064 {
1065 "Fixed ",
1066 "LowPri",
1067 "SMI ",
1068 "Rsvd ",
1069 "NMI ",
1070 "INIT ",
1071 "Rsvd ",
1072 "ExtINT"
1073 };
1074
1075 const uint64_t u64Rte = pThis->au64RedirTable[idxRte];
1076 const char *pszDestMode = IOAPIC_RTE_GET_DEST_MODE(u64Rte) == 0 ? "phys" : "log ";
1077 const uint8_t uDest = IOAPIC_RTE_GET_DEST(u64Rte);
1078 const uint8_t uMask = IOAPIC_RTE_GET_MASK(u64Rte);
1079 const char *pszTriggerMode = IOAPIC_RTE_GET_TRIGGER_MODE(u64Rte) == 0 ? "edge " : "level";
1080 const uint8_t uRemoteIrr = IOAPIC_RTE_GET_REMOTE_IRR(u64Rte);
1081 const char *pszPolarity = IOAPIC_RTE_GET_POLARITY(u64Rte) == 0 ? "acthi" : "actlo";
1082 const char *pszDeliveryStatus = IOAPIC_RTE_GET_DELIVERY_STATUS(u64Rte) == 0 ? "idle" : "pend";
1083 const uint8_t uDeliveryMode = IOAPIC_RTE_GET_DELIVERY_MODE(u64Rte);
1084 Assert(uDeliveryMode < RT_ELEMENTS(s_apszDeliveryModes));
1085 const char *pszDeliveryMode = s_apszDeliveryModes[uDeliveryMode];
1086 const uint8_t uVector = IOAPIC_RTE_GET_VECTOR(u64Rte);
1087
1088 pHlp->pfnPrintf(pHlp, " %02d %s %02x %u %u %s %u %s %s %s %3u (%016llx)\n",
1089 idxRte,
1090 pszDestMode,
1091 uDest,
1092 uMask,
1093 (pThis->uIrr >> idxRte) & 1,
1094 pszTriggerMode,
1095 uRemoteIrr,
1096 pszPolarity,
1097 pszDeliveryStatus,
1098 pszDeliveryMode,
1099 uVector,
1100 u64Rte);
1101 }
1102}
1103
1104
1105/**
1106 * @copydoc FNSSMDEVSAVEEXEC
1107 */
1108static DECLCALLBACK(int) ioapicR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1109{
1110 PCIOAPIC pThis = PDMDEVINS_2_DATA(pDevIns, PCIOAPIC);
1111 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1112 LogFlow(("IOAPIC: ioapicR3SaveExec\n"));
1113
1114 pHlp->pfnSSMPutU32(pSSM, pThis->uIrr);
1115 pHlp->pfnSSMPutU8(pSSM, pThis->u8Id);
1116 pHlp->pfnSSMPutU8(pSSM, pThis->u8Index);
1117 for (uint8_t idxRte = 0; idxRte < RT_ELEMENTS(pThis->au64RedirTable); idxRte++)
1118 pHlp->pfnSSMPutU64(pSSM, pThis->au64RedirTable[idxRte]);
1119
1120 return VINF_SUCCESS;
1121}
1122
1123
1124/**
1125 * @copydoc FNSSMDEVLOADEXEC
1126 */
1127static DECLCALLBACK(int) ioapicR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1128{
1129 PIOAPIC pThis = PDMDEVINS_2_DATA(pDevIns, PIOAPIC);
1130 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1131 LogFlow(("APIC: apicR3LoadExec: uVersion=%u uPass=%#x\n", uVersion, uPass));
1132
1133 Assert(uPass == SSM_PASS_FINAL);
1134 NOREF(uPass);
1135
1136 /* Weed out invalid versions. */
1137 if ( uVersion != IOAPIC_SAVED_STATE_VERSION
1138 && uVersion != IOAPIC_SAVED_STATE_VERSION_VBOX_50)
1139 {
1140 LogRel(("IOAPIC: ioapicR3LoadExec: Invalid/unrecognized saved-state version %u (%#x)\n", uVersion, uVersion));
1141 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
1142 }
1143
1144 if (uVersion == IOAPIC_SAVED_STATE_VERSION)
1145 pHlp->pfnSSMGetU32(pSSM, &pThis->uIrr);
1146
1147 pHlp->pfnSSMGetU8V(pSSM, &pThis->u8Id);
1148 pHlp->pfnSSMGetU8V(pSSM, &pThis->u8Index);
1149 for (uint8_t idxRte = 0; idxRte < RT_ELEMENTS(pThis->au64RedirTable); idxRte++)
1150 pHlp->pfnSSMGetU64(pSSM, &pThis->au64RedirTable[idxRte]);
1151
1152 return VINF_SUCCESS;
1153}
1154
1155
1156/**
1157 * @interface_method_impl{PDMDEVREG,pfnReset}
1158 */
1159static DECLCALLBACK(void) ioapicR3Reset(PPDMDEVINS pDevIns)
1160{
1161 PIOAPIC pThis = PDMDEVINS_2_DATA(pDevIns, PIOAPIC);
1162 PIOAPICCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PIOAPICCC);
1163 LogFlow(("IOAPIC: ioapicR3Reset: pThis=%p\n", pThis));
1164
1165 /* There might be devices threads calling ioapicSetIrq() in parallel, hence the lock. */
1166 IOAPIC_LOCK(pDevIns, pThis, pThisCC, VERR_IGNORED);
1167
1168 pThis->uIrr = 0;
1169 pThis->u8Index = 0;
1170 pThis->u8Id = 0;
1171
1172 for (uint8_t idxRte = 0; idxRte < RT_ELEMENTS(pThis->au64RedirTable); idxRte++)
1173 {
1174 pThis->au64RedirTable[idxRte] = IOAPIC_RTE_MASK;
1175 pThis->au32TagSrc[idxRte] = 0;
1176 }
1177
1178 IOAPIC_UNLOCK(pDevIns, pThis, pThisCC);
1179}
1180
1181
1182/**
1183 * @interface_method_impl{PDMDEVREG,pfnRelocate}
1184 */
1185static DECLCALLBACK(void) ioapicR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
1186{
1187 PIOAPICRC pThisRC = PDMINS_2_DATA_RC(pDevIns, PIOAPICRC);
1188 LogFlow(("IOAPIC: ioapicR3Relocate: pThis=%p offDelta=%RGi\n", PDMDEVINS_2_DATA(pDevIns, PIOAPIC), offDelta));
1189
1190 pThisRC->pIoApicHlp += offDelta;
1191}
1192
1193
1194/**
1195 * @interface_method_impl{PDMDEVREG,pfnDestruct}
1196 */
1197static DECLCALLBACK(int) ioapicR3Destruct(PPDMDEVINS pDevIns)
1198{
1199 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
1200 PIOAPIC pThis = PDMDEVINS_2_DATA(pDevIns, PIOAPIC);
1201 LogFlow(("IOAPIC: ioapicR3Destruct: pThis=%p\n", pThis));
1202
1203# ifndef IOAPIC_WITH_PDM_CRITSECT
1204 /*
1205 * Destroy the RTE critical section.
1206 */
1207 if (PDMCritSectIsInitialized(&pThis->CritSect))
1208 PDMR3CritSectDelete(&pThis->CritSect);
1209# else
1210 RT_NOREF_PV(pThis);
1211# endif
1212
1213 return VINF_SUCCESS;
1214}
1215
1216
1217/**
1218 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1219 */
1220static DECLCALLBACK(int) ioapicR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1221{
1222 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1223 PIOAPIC pThis = PDMDEVINS_2_DATA(pDevIns, PIOAPIC);
1224 PIOAPICCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PIOAPICCC);
1225 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1226 LogFlow(("IOAPIC: ioapicR3Construct: pThis=%p iInstance=%d\n", pThis, iInstance));
1227 Assert(iInstance == 0); RT_NOREF(iInstance);
1228
1229 /*
1230 * Validate and read the configuration.
1231 */
1232 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "NumCPUs|ChipType", "");
1233
1234 /* The number of CPUs is currently unused, but left in CFGM and saved-state in case an ID of 0 is
1235 upsets some guest which we haven't yet tested. */
1236 uint32_t cCpus;
1237 int rc = pHlp->pfnCFGMQueryU32Def(pCfg, "NumCPUs", &cCpus, 1);
1238 if (RT_FAILURE(rc))
1239 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to query integer value \"NumCPUs\""));
1240 pThis->cCpus = (uint8_t)cCpus;
1241
1242 char szChipType[16];
1243 rc = pHlp->pfnCFGMQueryStringDef(pCfg, "ChipType", &szChipType[0], sizeof(szChipType), "ICH9");
1244 if (RT_FAILURE(rc))
1245 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to query string value \"ChipType\""));
1246
1247 if (!strcmp(szChipType, "ICH9"))
1248 {
1249 /* Newer 2007-ish I/O APIC integrated into ICH southbridges. */
1250 pThis->u8ApicVer = IOAPIC_VERSION_ICH9;
1251 pThis->u8IdMask = 0xff;
1252 pThis->u8MaxRte = IOAPIC_MAX_RTE_INDEX;
1253 pThis->u8LastRteRegIdx = IOAPIC_INDIRECT_INDEX_RTE_END;
1254 pThis->u64RteWriteMask = IOAPIC_RTE_VALID_WRITE_MASK_ICH9;
1255 pThis->u64RteReadMask = IOAPIC_RTE_VALID_READ_MASK_ICH9;
1256 }
1257 else if (!strcmp(szChipType, "82093AA"))
1258 {
1259 /* Older 1995-ish discrete I/O APIC, used in P6 class systems. */
1260 pThis->u8ApicVer = IOAPIC_VERSION_82093AA;
1261 pThis->u8IdMask = 0x0f;
1262 pThis->u8MaxRte = IOAPIC_MAX_RTE_INDEX;
1263 pThis->u8LastRteRegIdx = IOAPIC_INDIRECT_INDEX_RTE_END;
1264 pThis->u64RteWriteMask = IOAPIC_RTE_VALID_WRITE_MASK_82093AA;
1265 pThis->u64RteReadMask = IOAPIC_RTE_VALID_READ_MASK_82093AA;
1266 }
1267 else if (!strcmp(szChipType, "82379AB"))
1268 {
1269 /* Even older 1993-ish I/O APIC built into SIO.A, used in EISA and early PCI systems. */
1270 /* Exact same version and behavior as 82093AA, only the number of RTEs is different. */
1271 pThis->u8ApicVer = IOAPIC_VERSION_82093AA;
1272 pThis->u8IdMask = 0x0f;
1273 pThis->u8MaxRte = IOAPIC_REDUCED_MAX_RTE_INDEX;
1274 pThis->u8LastRteRegIdx = IOAPIC_REDUCED_INDIRECT_INDEX_RTE_END;
1275 pThis->u64RteWriteMask = IOAPIC_RTE_VALID_WRITE_MASK_82093AA;
1276 pThis->u64RteReadMask = IOAPIC_RTE_VALID_READ_MASK_82093AA;
1277 }
1278 else
1279 return PDMDevHlpVMSetError(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES, RT_SRC_POS,
1280 N_("I/O APIC configuration error: The \"ChipType\" value \"%s\" is unsupported"), szChipType);
1281 Log2(("IOAPIC: cCpus=%u fRZEnabled=%RTbool szChipType=%s\n", cCpus, pDevIns->fR0Enabled | pDevIns->fRCEnabled, szChipType));
1282
1283 /*
1284 * We will use our own critical section for the IOAPIC device.
1285 */
1286 rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
1287 AssertRCReturn(rc, rc);
1288
1289# ifndef IOAPIC_WITH_PDM_CRITSECT
1290 /*
1291 * Setup the critical section to protect concurrent writes to the RTEs.
1292 */
1293 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "IOAPIC");
1294 AssertRCReturn(rc, rc);
1295# endif
1296
1297 /*
1298 * Register the IOAPIC.
1299 */
1300 PDMIOAPICREG IoApicReg;
1301 IoApicReg.u32Version = PDM_IOAPICREG_VERSION;
1302 IoApicReg.pfnSetIrq = ioapicSetIrq;
1303 IoApicReg.pfnSendMsi = ioapicSendMsi;
1304 IoApicReg.pfnSetEoi = ioapicSetEoi;
1305 IoApicReg.u32TheEnd = PDM_IOAPICREG_VERSION;
1306 rc = PDMDevHlpIoApicRegister(pDevIns, &IoApicReg, &pThisCC->pIoApicHlp);
1307 AssertRCReturn(rc, rc);
1308
1309 /*
1310 * Register MMIO region.
1311 */
1312 rc = PDMDevHlpMmioCreateAndMap(pDevIns, IOAPIC_MMIO_BASE_PHYSADDR, IOAPIC_MMIO_SIZE, ioapicMmioWrite, ioapicMmioRead,
1313 IOMMMIO_FLAGS_READ_DWORD | IOMMMIO_FLAGS_WRITE_DWORD_ZEROED, "I/O APIC", &pThis->hMmio);
1314 AssertRCReturn(rc, rc);
1315
1316 /*
1317 * Register the saved state.
1318 */
1319 rc = PDMDevHlpSSMRegister(pDevIns, IOAPIC_SAVED_STATE_VERSION, sizeof(*pThis), ioapicR3SaveExec, ioapicR3LoadExec);
1320 AssertRCReturn(rc, rc);
1321
1322 /*
1323 * Register debugger info item.
1324 */
1325 rc = PDMDevHlpDBGFInfoRegister(pDevIns, "ioapic", "Display IO APIC state.", ioapicR3DbgInfo);
1326 AssertRCReturn(rc, rc);
1327
1328 /*
1329 * Register debugger register access.
1330 */
1331 rc = PDMDevHlpDBGFRegRegister(pDevIns, g_aRegDesc);
1332 AssertRCReturn(rc, rc);
1333
1334# ifdef VBOX_WITH_STATISTICS
1335 /*
1336 * Statistics.
1337 */
1338 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMmioReadRZ, STAMTYPE_COUNTER, "RZ/MmioReadRZ", STAMUNIT_OCCURENCES, "Number of IOAPIC MMIO reads in RZ.");
1339 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMmioWriteRZ, STAMTYPE_COUNTER, "RZ/MmioWriteRZ", STAMUNIT_OCCURENCES, "Number of IOAPIC MMIO writes in RZ.");
1340 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatSetIrqRZ, STAMTYPE_COUNTER, "RZ/SetIrqRZ", STAMUNIT_OCCURENCES, "Number of IOAPIC SetIrq calls in RZ.");
1341 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatSetEoiRZ, STAMTYPE_COUNTER, "RZ/SetEoiRZ", STAMUNIT_OCCURENCES, "Number of IOAPIC SetEoi calls in RZ.");
1342
1343 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMmioReadR3, STAMTYPE_COUNTER, "R3/MmioReadR3", STAMUNIT_OCCURENCES, "Number of IOAPIC MMIO reads in R3");
1344 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMmioWriteR3, STAMTYPE_COUNTER, "R3/MmioWriteR3", STAMUNIT_OCCURENCES, "Number of IOAPIC MMIO writes in R3.");
1345 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatSetIrqR3, STAMTYPE_COUNTER, "R3/SetIrqR3", STAMUNIT_OCCURENCES, "Number of IOAPIC SetIrq calls in R3.");
1346 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatSetEoiR3, STAMTYPE_COUNTER, "R3/SetEoiR3", STAMUNIT_OCCURENCES, "Number of IOAPIC SetEoi calls in R3.");
1347
1348 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRedundantEdgeIntr, STAMTYPE_COUNTER, "RedundantEdgeIntr", STAMUNIT_OCCURENCES, "Number of redundant edge-triggered interrupts (no IRR change).");
1349 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRedundantLevelIntr, STAMTYPE_COUNTER, "RedundantLevelIntr", STAMUNIT_OCCURENCES, "Number of redundant level-triggered interrupts (no IRR change).");
1350 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatSuppressedLevelIntr, STAMTYPE_COUNTER, "SuppressedLevelIntr", STAMUNIT_OCCURENCES, "Number of suppressed level-triggered interrupts by remote IRR.");
1351
1352 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatEoiContention, STAMTYPE_COUNTER, "CritSect/ContentionSetEoi", STAMUNIT_OCCURENCES, "Number of times the critsect is busy during EOI writes causing trips to R3.");
1353 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatSetRteContention, STAMTYPE_COUNTER, "CritSect/ContentionSetRte", STAMUNIT_OCCURENCES, "Number of times the critsect is busy during RTE writes causing trips to R3.");
1354
1355 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatLevelIrqSent, STAMTYPE_COUNTER, "LevelIntr/Sent", STAMUNIT_OCCURENCES, "Number of level-triggered interrupts sent to the local APIC(s).");
1356 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatEoiReceived, STAMTYPE_COUNTER, "LevelIntr/Recv", STAMUNIT_OCCURENCES, "Number of EOIs received for level-triggered interrupts from the local APIC(s).");
1357# endif
1358
1359 /*
1360 * Init. the device state.
1361 */
1362 LogRel(("IOAPIC: Using implementation 2.0! Chipset type %s\n", szChipType));
1363 ioapicR3Reset(pDevIns);
1364
1365 return VINF_SUCCESS;
1366}
1367
1368#else /* !IN_RING3 */
1369
1370/**
1371 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
1372 */
1373static DECLCALLBACK(int) ioapicRZConstruct(PPDMDEVINS pDevIns)
1374{
1375 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1376 PIOAPIC pThis = PDMDEVINS_2_DATA(pDevIns, PIOAPIC);
1377 PIOAPICCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PIOAPICCC);
1378
1379 int rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
1380 AssertRCReturn(rc, rc);
1381
1382 PDMIOAPICREG IoApicReg;
1383 IoApicReg.u32Version = PDM_IOAPICREG_VERSION;
1384 IoApicReg.pfnSetIrq = ioapicSetIrq;
1385 IoApicReg.pfnSendMsi = ioapicSendMsi;
1386 IoApicReg.pfnSetEoi = ioapicSetEoi;
1387 IoApicReg.u32TheEnd = PDM_IOAPICREG_VERSION;
1388 rc = PDMDevHlpIoApicSetUpContext(pDevIns, &IoApicReg, &pThisCC->pIoApicHlp);
1389 AssertRCReturn(rc, rc);
1390
1391 rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hMmio, ioapicMmioWrite, ioapicMmioRead, NULL /*pvUser*/);
1392 AssertRCReturn(rc, rc);
1393
1394 return VINF_SUCCESS;
1395}
1396
1397#endif /* !IN_RING3 */
1398
1399/**
1400 * IO APIC device registration structure.
1401 */
1402const PDMDEVREG g_DeviceIOAPIC =
1403{
1404 /* .u32Version = */ PDM_DEVREG_VERSION,
1405 /* .uReserved0 = */ 0,
1406 /* .szName = */ "ioapic",
1407 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE
1408 | PDM_DEVREG_FLAGS_REQUIRE_R0 | PDM_DEVREG_FLAGS_REQUIRE_RC,
1409 /* .fClass = */ PDM_DEVREG_CLASS_PIC,
1410 /* .cMaxInstances = */ 1,
1411 /* .uSharedVersion = */ 42,
1412 /* .cbInstanceShared = */ sizeof(IOAPIC),
1413 /* .cbInstanceCC = */ sizeof(IOAPICCC),
1414 /* .cbInstanceRC = */ sizeof(IOAPICRC),
1415 /* .cMaxPciDevices = */ 0,
1416 /* .cMaxMsixVectors = */ 0,
1417 /* .pszDescription = */ "I/O Advanced Programmable Interrupt Controller (IO-APIC) Device",
1418#if defined(IN_RING3)
1419 /* .pszRCMod = */ "VBoxDDRC.rc",
1420 /* .pszR0Mod = */ "VBoxDDR0.r0",
1421 /* .pfnConstruct = */ ioapicR3Construct,
1422 /* .pfnDestruct = */ ioapicR3Destruct,
1423 /* .pfnRelocate = */ ioapicR3Relocate,
1424 /* .pfnMemSetup = */ NULL,
1425 /* .pfnPowerOn = */ NULL,
1426 /* .pfnReset = */ ioapicR3Reset,
1427 /* .pfnSuspend = */ NULL,
1428 /* .pfnResume = */ NULL,
1429 /* .pfnAttach = */ NULL,
1430 /* .pfnDetach = */ NULL,
1431 /* .pfnQueryInterface = */ NULL,
1432 /* .pfnInitComplete = */ NULL,
1433 /* .pfnPowerOff = */ NULL,
1434 /* .pfnSoftReset = */ NULL,
1435 /* .pfnReserved0 = */ NULL,
1436 /* .pfnReserved1 = */ NULL,
1437 /* .pfnReserved2 = */ NULL,
1438 /* .pfnReserved3 = */ NULL,
1439 /* .pfnReserved4 = */ NULL,
1440 /* .pfnReserved5 = */ NULL,
1441 /* .pfnReserved6 = */ NULL,
1442 /* .pfnReserved7 = */ NULL,
1443#elif defined(IN_RING0)
1444 /* .pfnEarlyConstruct = */ NULL,
1445 /* .pfnConstruct = */ ioapicRZConstruct,
1446 /* .pfnDestruct = */ NULL,
1447 /* .pfnFinalDestruct = */ NULL,
1448 /* .pfnRequest = */ NULL,
1449 /* .pfnReserved0 = */ NULL,
1450 /* .pfnReserved1 = */ NULL,
1451 /* .pfnReserved2 = */ NULL,
1452 /* .pfnReserved3 = */ NULL,
1453 /* .pfnReserved4 = */ NULL,
1454 /* .pfnReserved5 = */ NULL,
1455 /* .pfnReserved6 = */ NULL,
1456 /* .pfnReserved7 = */ NULL,
1457#elif defined(IN_RC)
1458 /* .pfnConstruct = */ ioapicRZConstruct,
1459 /* .pfnReserved0 = */ NULL,
1460 /* .pfnReserved1 = */ NULL,
1461 /* .pfnReserved2 = */ NULL,
1462 /* .pfnReserved3 = */ NULL,
1463 /* .pfnReserved4 = */ NULL,
1464 /* .pfnReserved5 = */ NULL,
1465 /* .pfnReserved6 = */ NULL,
1466 /* .pfnReserved7 = */ NULL,
1467#else
1468# error "Not in IN_RING3, IN_RING0 or IN_RC!"
1469#endif
1470 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
1471};
1472
1473
1474#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
1475
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