VirtualBox

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

Last change on this file since 86070 was 86070, checked in by vboxsync, 4 years ago

AMD IOMMU: bugref:9654 Fix accidentally not copying/initialize MSIMSG values. Fix using the translated memory address on
successful translations from the IOMMU. Fix not remapping any interrupt when IOMMU code is compiled but an IOMMU is not present.

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