VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/APICAll.cpp@ 100765

Last change on this file since 100765 was 99739, checked in by vboxsync, 20 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 130.7 KB
Line 
1/* $Id: APICAll.cpp 99739 2023-05-11 01:01:08Z vboxsync $ */
2/** @file
3 * APIC - Advanced Programmable Interrupt Controller - All Contexts.
4 */
5
6/*
7 * Copyright (C) 2016-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_DEV_APIC
33#define VMCPU_INCL_CPUM_GST_CTX /* for macOS hack */
34#include "APICInternal.h"
35#include <VBox/vmm/apic.h>
36#include <VBox/vmm/pdmdev.h>
37#include <VBox/vmm/pdmapi.h>
38#include <VBox/vmm/vmcc.h>
39#include <VBox/vmm/vmm.h>
40#include <VBox/vmm/vmcpuset.h>
41#ifdef IN_RING0
42# include <VBox/vmm/gvmm.h>
43#endif
44
45
46/*********************************************************************************************************************************
47* Internal Functions *
48*********************************************************************************************************************************/
49static void apicSetInterruptFF(PVMCPUCC pVCpu, PDMAPICIRQ enmType);
50static void apicStopTimer(PVMCPUCC pVCpu);
51
52
53/*********************************************************************************************************************************
54* Global Variables *
55*********************************************************************************************************************************/
56#if XAPIC_HARDWARE_VERSION == XAPIC_HARDWARE_VERSION_P4
57/** An ordered array of valid LVT masks. */
58static const uint32_t g_au32LvtValidMasks[] =
59{
60 XAPIC_LVT_TIMER_VALID,
61 XAPIC_LVT_THERMAL_VALID,
62 XAPIC_LVT_PERF_VALID,
63 XAPIC_LVT_LINT_VALID, /* LINT0 */
64 XAPIC_LVT_LINT_VALID, /* LINT1 */
65 XAPIC_LVT_ERROR_VALID
66};
67#endif
68
69#if 0
70/** @todo CMCI */
71static const uint32_t g_au32LvtExtValidMask[] =
72{
73 XAPIC_LVT_CMCI_VALID
74};
75#endif
76
77
78/**
79 * Checks if a vector is set in an APIC 256-bit sparse register.
80 *
81 * @returns true if the specified vector is set, false otherwise.
82 * @param pApicReg The APIC 256-bit spare register.
83 * @param uVector The vector to check if set.
84 */
85DECLINLINE(bool) apicTestVectorInReg(const volatile XAPIC256BITREG *pApicReg, uint8_t uVector)
86{
87 const volatile uint8_t *pbBitmap = (const volatile uint8_t *)&pApicReg->u[0];
88 return ASMBitTest(pbBitmap, (XAPIC_REG256_VECTOR_OFF(uVector) << 3) + XAPIC_REG256_VECTOR_BIT(uVector));
89}
90
91
92/**
93 * Sets the vector in an APIC 256-bit sparse register.
94 *
95 * @param pApicReg The APIC 256-bit spare register.
96 * @param uVector The vector to set.
97 */
98DECLINLINE(void) apicSetVectorInReg(volatile XAPIC256BITREG *pApicReg, uint8_t uVector)
99{
100 volatile uint8_t *pbBitmap = (volatile uint8_t *)&pApicReg->u[0];
101 ASMAtomicBitSet(pbBitmap, (XAPIC_REG256_VECTOR_OFF(uVector) << 3) + XAPIC_REG256_VECTOR_BIT(uVector));
102}
103
104
105/**
106 * Clears the vector in an APIC 256-bit sparse register.
107 *
108 * @param pApicReg The APIC 256-bit spare register.
109 * @param uVector The vector to clear.
110 */
111DECLINLINE(void) apicClearVectorInReg(volatile XAPIC256BITREG *pApicReg, uint8_t uVector)
112{
113 volatile uint8_t *pbBitmap = (volatile uint8_t *)&pApicReg->u[0];
114 ASMAtomicBitClear(pbBitmap, (XAPIC_REG256_VECTOR_OFF(uVector) << 3) + XAPIC_REG256_VECTOR_BIT(uVector));
115}
116
117
118#if 0 /* unused */
119/**
120 * Checks if a vector is set in an APIC Pending-Interrupt Bitmap (PIB).
121 *
122 * @returns true if the specified vector is set, false otherwise.
123 * @param pvPib Opaque pointer to the PIB.
124 * @param uVector The vector to check if set.
125 */
126DECLINLINE(bool) apicTestVectorInPib(volatile void *pvPib, uint8_t uVector)
127{
128 return ASMBitTest(pvPib, uVector);
129}
130#endif /* unused */
131
132
133/**
134 * Atomically sets the PIB notification bit.
135 *
136 * @returns non-zero if the bit was already set, 0 otherwise.
137 * @param pApicPib Pointer to the PIB.
138 */
139DECLINLINE(uint32_t) apicSetNotificationBitInPib(PAPICPIB pApicPib)
140{
141 return ASMAtomicXchgU32(&pApicPib->fOutstandingNotification, RT_BIT_32(31));
142}
143
144
145/**
146 * Atomically tests and clears the PIB notification bit.
147 *
148 * @returns non-zero if the bit was already set, 0 otherwise.
149 * @param pApicPib Pointer to the PIB.
150 */
151DECLINLINE(uint32_t) apicClearNotificationBitInPib(PAPICPIB pApicPib)
152{
153 return ASMAtomicXchgU32(&pApicPib->fOutstandingNotification, UINT32_C(0));
154}
155
156
157/**
158 * Sets the vector in an APIC Pending-Interrupt Bitmap (PIB).
159 *
160 * @param pvPib Opaque pointer to the PIB.
161 * @param uVector The vector to set.
162 */
163DECLINLINE(void) apicSetVectorInPib(volatile void *pvPib, uint8_t uVector)
164{
165 ASMAtomicBitSet(pvPib, uVector);
166}
167
168#if 0 /* unused */
169/**
170 * Clears the vector in an APIC Pending-Interrupt Bitmap (PIB).
171 *
172 * @param pvPib Opaque pointer to the PIB.
173 * @param uVector The vector to clear.
174 */
175DECLINLINE(void) apicClearVectorInPib(volatile void *pvPib, uint8_t uVector)
176{
177 ASMAtomicBitClear(pvPib, uVector);
178}
179#endif /* unused */
180
181#if 0 /* unused */
182/**
183 * Atomically OR's a fragment (32 vectors) into an APIC 256-bit sparse
184 * register.
185 *
186 * @param pApicReg The APIC 256-bit spare register.
187 * @param idxFragment The index of the 32-bit fragment in @a
188 * pApicReg.
189 * @param u32Fragment The 32-bit vector fragment to OR.
190 */
191DECLINLINE(void) apicOrVectorsToReg(volatile XAPIC256BITREG *pApicReg, size_t idxFragment, uint32_t u32Fragment)
192{
193 Assert(idxFragment < RT_ELEMENTS(pApicReg->u));
194 ASMAtomicOrU32(&pApicReg->u[idxFragment].u32Reg, u32Fragment);
195}
196#endif /* unused */
197
198
199#if 0 /* unused */
200/**
201 * Atomically AND's a fragment (32 vectors) into an APIC
202 * 256-bit sparse register.
203 *
204 * @param pApicReg The APIC 256-bit spare register.
205 * @param idxFragment The index of the 32-bit fragment in @a
206 * pApicReg.
207 * @param u32Fragment The 32-bit vector fragment to AND.
208 */
209DECLINLINE(void) apicAndVectorsToReg(volatile XAPIC256BITREG *pApicReg, size_t idxFragment, uint32_t u32Fragment)
210{
211 Assert(idxFragment < RT_ELEMENTS(pApicReg->u));
212 ASMAtomicAndU32(&pApicReg->u[idxFragment].u32Reg, u32Fragment);
213}
214#endif /* unused */
215
216
217/**
218 * Reports and returns appropriate error code for invalid MSR accesses.
219 *
220 * @returns VERR_CPUM_RAISE_GP_0
221 *
222 * @param pVCpu The cross context virtual CPU structure.
223 * @param u32Reg The MSR being accessed.
224 * @param enmAccess The invalid-access type.
225 */
226static int apicMsrAccessError(PVMCPUCC pVCpu, uint32_t u32Reg, APICMSRACCESS enmAccess)
227{
228 static struct
229 {
230 const char *pszBefore; /* The error message before printing the MSR index */
231 const char *pszAfter; /* The error message after printing the MSR index */
232 } const s_aAccess[] =
233 {
234 /* enmAccess pszBefore pszAfter */
235 /* 0 */ { "read MSR", " while not in x2APIC mode" },
236 /* 1 */ { "write MSR", " while not in x2APIC mode" },
237 /* 2 */ { "read reserved/unknown MSR", "" },
238 /* 3 */ { "write reserved/unknown MSR", "" },
239 /* 4 */ { "read write-only MSR", "" },
240 /* 5 */ { "write read-only MSR", "" },
241 /* 6 */ { "read reserved bits of MSR", "" },
242 /* 7 */ { "write reserved bits of MSR", "" },
243 /* 8 */ { "write an invalid value to MSR", "" },
244 /* 9 */ { "write MSR", " disallowed by configuration" },
245 /* 10 */ { "read MSR", " disallowed by configuration" },
246 };
247 AssertCompile(RT_ELEMENTS(s_aAccess) == APICMSRACCESS_COUNT);
248
249 size_t const i = enmAccess;
250 Assert(i < RT_ELEMENTS(s_aAccess));
251 if (pVCpu->apic.s.cLogMaxAccessError++ < 5)
252 LogRel(("APIC%u: Attempt to %s (%#x)%s -> #GP(0)\n", pVCpu->idCpu, s_aAccess[i].pszBefore, u32Reg, s_aAccess[i].pszAfter));
253 return VERR_CPUM_RAISE_GP_0;
254}
255
256
257/**
258 * Gets the descriptive APIC mode.
259 *
260 * @returns The name.
261 * @param enmMode The xAPIC mode.
262 */
263const char *apicGetModeName(APICMODE enmMode)
264{
265 switch (enmMode)
266 {
267 case APICMODE_DISABLED: return "Disabled";
268 case APICMODE_XAPIC: return "xAPIC";
269 case APICMODE_X2APIC: return "x2APIC";
270 default: break;
271 }
272 return "Invalid";
273}
274
275
276/**
277 * Gets the descriptive destination format name.
278 *
279 * @returns The destination format name.
280 * @param enmDestFormat The destination format.
281 */
282const char *apicGetDestFormatName(XAPICDESTFORMAT enmDestFormat)
283{
284 switch (enmDestFormat)
285 {
286 case XAPICDESTFORMAT_FLAT: return "Flat";
287 case XAPICDESTFORMAT_CLUSTER: return "Cluster";
288 default: break;
289 }
290 return "Invalid";
291}
292
293
294/**
295 * Gets the descriptive delivery mode name.
296 *
297 * @returns The delivery mode name.
298 * @param enmDeliveryMode The delivery mode.
299 */
300const char *apicGetDeliveryModeName(XAPICDELIVERYMODE enmDeliveryMode)
301{
302 switch (enmDeliveryMode)
303 {
304 case XAPICDELIVERYMODE_FIXED: return "Fixed";
305 case XAPICDELIVERYMODE_LOWEST_PRIO: return "Lowest-priority";
306 case XAPICDELIVERYMODE_SMI: return "SMI";
307 case XAPICDELIVERYMODE_NMI: return "NMI";
308 case XAPICDELIVERYMODE_INIT: return "INIT";
309 case XAPICDELIVERYMODE_STARTUP: return "SIPI";
310 case XAPICDELIVERYMODE_EXTINT: return "ExtINT";
311 default: break;
312 }
313 return "Invalid";
314}
315
316
317/**
318 * Gets the descriptive destination mode name.
319 *
320 * @returns The destination mode name.
321 * @param enmDestMode The destination mode.
322 */
323const char *apicGetDestModeName(XAPICDESTMODE enmDestMode)
324{
325 switch (enmDestMode)
326 {
327 case XAPICDESTMODE_PHYSICAL: return "Physical";
328 case XAPICDESTMODE_LOGICAL: return "Logical";
329 default: break;
330 }
331 return "Invalid";
332}
333
334
335/**
336 * Gets the descriptive trigger mode name.
337 *
338 * @returns The trigger mode name.
339 * @param enmTriggerMode The trigger mode.
340 */
341const char *apicGetTriggerModeName(XAPICTRIGGERMODE enmTriggerMode)
342{
343 switch (enmTriggerMode)
344 {
345 case XAPICTRIGGERMODE_EDGE: return "Edge";
346 case XAPICTRIGGERMODE_LEVEL: return "Level";
347 default: break;
348 }
349 return "Invalid";
350}
351
352
353/**
354 * Gets the destination shorthand name.
355 *
356 * @returns The destination shorthand name.
357 * @param enmDestShorthand The destination shorthand.
358 */
359const char *apicGetDestShorthandName(XAPICDESTSHORTHAND enmDestShorthand)
360{
361 switch (enmDestShorthand)
362 {
363 case XAPICDESTSHORTHAND_NONE: return "None";
364 case XAPICDESTSHORTHAND_SELF: return "Self";
365 case XAPIDDESTSHORTHAND_ALL_INCL_SELF: return "All including self";
366 case XAPICDESTSHORTHAND_ALL_EXCL_SELF: return "All excluding self";
367 default: break;
368 }
369 return "Invalid";
370}
371
372
373/**
374 * Gets the timer mode name.
375 *
376 * @returns The timer mode name.
377 * @param enmTimerMode The timer mode.
378 */
379const char *apicGetTimerModeName(XAPICTIMERMODE enmTimerMode)
380{
381 switch (enmTimerMode)
382 {
383 case XAPICTIMERMODE_ONESHOT: return "One-shot";
384 case XAPICTIMERMODE_PERIODIC: return "Periodic";
385 case XAPICTIMERMODE_TSC_DEADLINE: return "TSC deadline";
386 default: break;
387 }
388 return "Invalid";
389}
390
391
392/**
393 * Gets the APIC mode given the base MSR value.
394 *
395 * @returns The APIC mode.
396 * @param uApicBaseMsr The APIC Base MSR value.
397 */
398APICMODE apicGetMode(uint64_t uApicBaseMsr)
399{
400 uint32_t const uMode = (uApicBaseMsr >> 10) & UINT64_C(3);
401 APICMODE const enmMode = (APICMODE)uMode;
402#ifdef VBOX_STRICT
403 /* Paranoia. */
404 switch (uMode)
405 {
406 case APICMODE_DISABLED:
407 case APICMODE_INVALID:
408 case APICMODE_XAPIC:
409 case APICMODE_X2APIC:
410 break;
411 default:
412 AssertMsgFailed(("Invalid mode"));
413 }
414#endif
415 return enmMode;
416}
417
418
419/**
420 * Returns whether the APIC is hardware enabled or not.
421 *
422 * @returns true if enabled, false otherwise.
423 * @param pVCpu The cross context virtual CPU structure.
424 */
425VMM_INT_DECL(bool) APICIsEnabled(PCVMCPUCC pVCpu)
426{
427 PCAPICCPU pApicCpu = VMCPU_TO_APICCPU(pVCpu);
428 return RT_BOOL(pApicCpu->uApicBaseMsr & MSR_IA32_APICBASE_EN);
429}
430
431
432/**
433 * Finds the most significant set bit in an APIC 256-bit sparse register.
434 *
435 * @returns @a rcNotFound if no bit was set, 0-255 otherwise.
436 * @param pReg The APIC 256-bit sparse register.
437 * @param rcNotFound What to return when no bit is set.
438 */
439static int apicGetHighestSetBitInReg(volatile const XAPIC256BITREG *pReg, int rcNotFound)
440{
441 ssize_t const cFragments = RT_ELEMENTS(pReg->u);
442 unsigned const uFragmentShift = 5;
443 AssertCompile(1 << uFragmentShift == sizeof(pReg->u[0].u32Reg) * 8);
444 for (ssize_t i = cFragments - 1; i >= 0; i--)
445 {
446 uint32_t const uFragment = pReg->u[i].u32Reg;
447 if (uFragment)
448 {
449 unsigned idxSetBit = ASMBitLastSetU32(uFragment);
450 --idxSetBit;
451 idxSetBit |= i << uFragmentShift;
452 return idxSetBit;
453 }
454 }
455 return rcNotFound;
456}
457
458
459/**
460 * Reads a 32-bit register at a specified offset.
461 *
462 * @returns The value at the specified offset.
463 * @param pXApicPage The xAPIC page.
464 * @param offReg The offset of the register being read.
465 */
466DECLINLINE(uint32_t) apicReadRaw32(PCXAPICPAGE pXApicPage, uint16_t offReg)
467{
468 Assert(offReg < sizeof(*pXApicPage) - sizeof(uint32_t));
469 uint8_t const *pbXApic = (const uint8_t *)pXApicPage;
470 uint32_t const uValue = *(const uint32_t *)(pbXApic + offReg);
471 return uValue;
472}
473
474
475/**
476 * Writes a 32-bit register at a specified offset.
477 *
478 * @param pXApicPage The xAPIC page.
479 * @param offReg The offset of the register being written.
480 * @param uReg The value of the register.
481 */
482DECLINLINE(void) apicWriteRaw32(PXAPICPAGE pXApicPage, uint16_t offReg, uint32_t uReg)
483{
484 Assert(offReg < sizeof(*pXApicPage) - sizeof(uint32_t));
485 uint8_t *pbXApic = (uint8_t *)pXApicPage;
486 *(uint32_t *)(pbXApic + offReg) = uReg;
487}
488
489
490/**
491 * Sets an error in the internal ESR of the specified APIC.
492 *
493 * @param pVCpu The cross context virtual CPU structure.
494 * @param uError The error.
495 * @thread Any.
496 */
497DECLINLINE(void) apicSetError(PVMCPUCC pVCpu, uint32_t uError)
498{
499 PAPICCPU pApicCpu = VMCPU_TO_APICCPU(pVCpu);
500 ASMAtomicOrU32(&pApicCpu->uEsrInternal, uError);
501}
502
503
504/**
505 * Clears all errors in the internal ESR.
506 *
507 * @returns The value of the internal ESR before clearing.
508 * @param pVCpu The cross context virtual CPU structure.
509 */
510DECLINLINE(uint32_t) apicClearAllErrors(PVMCPUCC pVCpu)
511{
512 VMCPU_ASSERT_EMT(pVCpu);
513 PAPICCPU pApicCpu = VMCPU_TO_APICCPU(pVCpu);
514 return ASMAtomicXchgU32(&pApicCpu->uEsrInternal, 0);
515}
516
517
518/**
519 * Signals the guest if a pending interrupt is ready to be serviced.
520 *
521 * @param pVCpu The cross context virtual CPU structure.
522 */
523static void apicSignalNextPendingIntr(PVMCPUCC pVCpu)
524{
525 VMCPU_ASSERT_EMT_OR_NOT_RUNNING(pVCpu);
526
527 PCXAPICPAGE pXApicPage = VMCPU_TO_CXAPICPAGE(pVCpu);
528 if (pXApicPage->svr.u.fApicSoftwareEnable)
529 {
530 int const irrv = apicGetHighestSetBitInReg(&pXApicPage->irr, -1 /* rcNotFound */);
531 if (irrv >= 0)
532 {
533 Assert(irrv <= (int)UINT8_MAX);
534 uint8_t const uVector = irrv;
535 int const isrv = apicGetHighestSetBitInReg(&pXApicPage->isr, 0 /* rcNotFound */);
536 Assert(isrv <= (int)UINT8_MAX);
537 uint8_t const uIsrVec = isrv;
538
539 /* uIsrVect reflects the highest interrupt vector currently serviced (i.e. in ISR),
540 * or zero if there's none. We want to report a pending interrupt only if IRR > ISR but
541 * regardless of TPR. Hence we can't look at the PPR value, since that also reflects TPR.
542 * NB: The APIC emulation will know when ISR changes, but not necessarily when TPR does.
543 */
544 if (XAPIC_PPR_GET_PP(uVector) > XAPIC_PPR_GET_PP(uIsrVec))
545 {
546 Log2(("APIC%u: apicSignalNextPendingIntr: Signalling pending interrupt. uVector=%#x\n", pVCpu->idCpu, uVector));
547 apicSetInterruptFF(pVCpu, PDMAPICIRQ_HARDWARE);
548 }
549 else
550 Log2(("APIC%u: apicSignalNextPendingIntr: Nothing to signal yet. uVector=%#x uIsrVec=%#x\n", pVCpu->idCpu, uVector, uIsrVec));
551 }
552 }
553 else
554 {
555 Log2(("APIC%u: apicSignalNextPendingIntr: APIC software-disabled, clearing pending interrupt\n", pVCpu->idCpu));
556 apicClearInterruptFF(pVCpu, PDMAPICIRQ_HARDWARE);
557 }
558}
559
560
561/**
562 * Sets the Spurious-Interrupt Vector Register (SVR).
563 *
564 * @returns VINF_SUCCESS or VERR_CPUM_RAISE_GP_0.
565 * @param pVCpu The cross context virtual CPU structure.
566 * @param uSvr The SVR value.
567 */
568static int apicSetSvr(PVMCPUCC pVCpu, uint32_t uSvr)
569{
570 VMCPU_ASSERT_EMT(pVCpu);
571
572 uint32_t uValidMask = XAPIC_SVR_VALID;
573 PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
574 if (pXApicPage->version.u.fEoiBroadcastSupression)
575 uValidMask |= XAPIC_SVR_SUPRESS_EOI_BROADCAST;
576
577 if ( XAPIC_IN_X2APIC_MODE(pVCpu)
578 && (uSvr & ~uValidMask))
579 return apicMsrAccessError(pVCpu, MSR_IA32_X2APIC_SVR, APICMSRACCESS_WRITE_RSVD_BITS);
580
581 Log2(("APIC%u: apicSetSvr: uSvr=%#RX32\n", pVCpu->idCpu, uSvr));
582 apicWriteRaw32(pXApicPage, XAPIC_OFF_SVR, uSvr);
583 if (!pXApicPage->svr.u.fApicSoftwareEnable)
584 {
585 /** @todo CMCI. */
586 pXApicPage->lvt_timer.u.u1Mask = 1;
587#if XAPIC_HARDWARE_VERSION == XAPIC_HARDWARE_VERSION_P4
588 pXApicPage->lvt_thermal.u.u1Mask = 1;
589#endif
590 pXApicPage->lvt_perf.u.u1Mask = 1;
591 pXApicPage->lvt_lint0.u.u1Mask = 1;
592 pXApicPage->lvt_lint1.u.u1Mask = 1;
593 pXApicPage->lvt_error.u.u1Mask = 1;
594 }
595
596 apicSignalNextPendingIntr(pVCpu);
597 return VINF_SUCCESS;
598}
599
600
601/**
602 * Sends an interrupt to one or more APICs.
603 *
604 * @returns Strict VBox status code.
605 * @param pVM The cross context VM structure.
606 * @param pVCpu The cross context virtual CPU structure, can be
607 * NULL if the source of the interrupt is not an
608 * APIC (for e.g. a bus).
609 * @param uVector The interrupt vector.
610 * @param enmTriggerMode The trigger mode.
611 * @param enmDeliveryMode The delivery mode.
612 * @param pDestCpuSet The destination CPU set.
613 * @param pfIntrAccepted Where to store whether this interrupt was
614 * accepted by the target APIC(s) or not.
615 * Optional, can be NULL.
616 * @param uSrcTag The interrupt source tag (debugging).
617 * @param rcRZ The return code if the operation cannot be
618 * performed in the current context.
619 */
620static VBOXSTRICTRC apicSendIntr(PVMCC pVM, PVMCPUCC pVCpu, uint8_t uVector, XAPICTRIGGERMODE enmTriggerMode,
621 XAPICDELIVERYMODE enmDeliveryMode, PCVMCPUSET pDestCpuSet, bool *pfIntrAccepted,
622 uint32_t uSrcTag, int rcRZ)
623{
624 AssertCompile(sizeof(pVM->apic.s) <= sizeof(pVM->apic.padding));
625 AssertCompile(sizeof(pVCpu->apic.s) <= sizeof(pVCpu->apic.padding));
626#ifdef IN_RING0
627 AssertCompile(sizeof(pVM->apicr0.s) <= sizeof(pVM->apicr0.padding));
628#endif
629 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
630 VMCPUID const cCpus = pVM->cCpus;
631 bool fAccepted = false;
632 switch (enmDeliveryMode)
633 {
634 case XAPICDELIVERYMODE_FIXED:
635 {
636 for (VMCPUID idCpu = 0; idCpu < cCpus; idCpu++)
637 if (VMCPUSET_IS_PRESENT(pDestCpuSet, idCpu))
638 {
639 PVMCPUCC pItVCpu = pVM->CTX_SUFF(apCpus)[idCpu];
640 if (APICIsEnabled(pItVCpu))
641 fAccepted = apicPostInterrupt(pItVCpu, uVector, enmTriggerMode, uSrcTag);
642 }
643 break;
644 }
645
646 case XAPICDELIVERYMODE_LOWEST_PRIO:
647 {
648 VMCPUID const idCpu = VMCPUSET_FIND_FIRST_PRESENT(pDestCpuSet);
649 AssertMsgBreak(idCpu < pVM->cCpus, ("APIC: apicSendIntr: No CPU found for lowest-priority delivery mode! idCpu=%u\n", idCpu));
650 PVMCPUCC pVCpuDst = pVM->CTX_SUFF(apCpus)[idCpu];
651 if (APICIsEnabled(pVCpuDst))
652 fAccepted = apicPostInterrupt(pVCpuDst, uVector, enmTriggerMode, uSrcTag);
653 else
654 AssertMsgFailed(("APIC: apicSendIntr: Target APIC not enabled in lowest-priority delivery mode! idCpu=%u\n", idCpu));
655 break;
656 }
657
658 case XAPICDELIVERYMODE_SMI:
659 {
660 for (VMCPUID idCpu = 0; idCpu < cCpus; idCpu++)
661 if (VMCPUSET_IS_PRESENT(pDestCpuSet, idCpu))
662 {
663 Log2(("APIC: apicSendIntr: Raising SMI on VCPU%u\n", idCpu));
664 apicSetInterruptFF(pVM->CTX_SUFF(apCpus)[idCpu], PDMAPICIRQ_SMI);
665 fAccepted = true;
666 }
667 break;
668 }
669
670 case XAPICDELIVERYMODE_NMI:
671 {
672 for (VMCPUID idCpu = 0; idCpu < cCpus; idCpu++)
673 if (VMCPUSET_IS_PRESENT(pDestCpuSet, idCpu))
674 {
675 PVMCPUCC pItVCpu = pVM->CTX_SUFF(apCpus)[idCpu];
676 if (APICIsEnabled(pItVCpu))
677 {
678 Log2(("APIC: apicSendIntr: Raising NMI on VCPU%u\n", idCpu));
679 apicSetInterruptFF(pItVCpu, PDMAPICIRQ_NMI);
680 fAccepted = true;
681 }
682 }
683 break;
684 }
685
686 case XAPICDELIVERYMODE_INIT:
687 {
688#ifdef IN_RING3
689 for (VMCPUID idCpu = 0; idCpu < cCpus; idCpu++)
690 if (VMCPUSET_IS_PRESENT(pDestCpuSet, idCpu))
691 {
692 Log2(("APIC: apicSendIntr: Issuing INIT to VCPU%u\n", idCpu));
693 VMMR3SendInitIpi(pVM, idCpu);
694 fAccepted = true;
695 }
696#else
697 /* We need to return to ring-3 to deliver the INIT. */
698 rcStrict = rcRZ;
699 fAccepted = true;
700#endif
701 break;
702 }
703
704 case XAPICDELIVERYMODE_STARTUP:
705 {
706#ifdef IN_RING3
707 for (VMCPUID idCpu = 0; idCpu < cCpus; idCpu++)
708 if (VMCPUSET_IS_PRESENT(pDestCpuSet, idCpu))
709 {
710 Log2(("APIC: apicSendIntr: Issuing SIPI to VCPU%u\n", idCpu));
711 VMMR3SendStartupIpi(pVM, idCpu, uVector);
712 fAccepted = true;
713 }
714#else
715 /* We need to return to ring-3 to deliver the SIPI. */
716 rcStrict = rcRZ;
717 fAccepted = true;
718 Log2(("APIC: apicSendIntr: SIPI issued, returning to RZ. rc=%Rrc\n", rcRZ));
719#endif
720 break;
721 }
722
723 case XAPICDELIVERYMODE_EXTINT:
724 {
725 for (VMCPUID idCpu = 0; idCpu < cCpus; idCpu++)
726 if (VMCPUSET_IS_PRESENT(pDestCpuSet, idCpu))
727 {
728 Log2(("APIC: apicSendIntr: Raising EXTINT on VCPU%u\n", idCpu));
729 apicSetInterruptFF(pVM->CTX_SUFF(apCpus)[idCpu], PDMAPICIRQ_EXTINT);
730 fAccepted = true;
731 }
732 break;
733 }
734
735 default:
736 {
737 AssertMsgFailed(("APIC: apicSendIntr: Unsupported delivery mode %#x (%s)\n", enmDeliveryMode,
738 apicGetDeliveryModeName(enmDeliveryMode)));
739 break;
740 }
741 }
742
743 /*
744 * If an illegal vector is programmed, set the 'send illegal vector' error here if the
745 * interrupt is being sent by an APIC.
746 *
747 * The 'receive illegal vector' will be set on the target APIC when the interrupt
748 * gets generated, see apicPostInterrupt().
749 *
750 * See Intel spec. 10.5.3 "Error Handling".
751 */
752 if ( rcStrict != rcRZ
753 && pVCpu)
754 {
755 /*
756 * Flag only errors when the delivery mode is fixed and not others.
757 *
758 * Ubuntu 10.04-3 amd64 live CD with 2 VCPUs gets upset as it sends an SIPI to the
759 * 2nd VCPU with vector 6 and checks the ESR for no errors, see @bugref{8245#c86}.
760 */
761 /** @todo The spec says this for LVT, but not explcitly for ICR-lo
762 * but it probably is true. */
763 if (enmDeliveryMode == XAPICDELIVERYMODE_FIXED)
764 {
765 if (RT_UNLIKELY(uVector <= XAPIC_ILLEGAL_VECTOR_END))
766 apicSetError(pVCpu, XAPIC_ESR_SEND_ILLEGAL_VECTOR);
767 }
768 }
769
770 if (pfIntrAccepted)
771 *pfIntrAccepted = fAccepted;
772
773 return rcStrict;
774}
775
776
777/**
778 * Checks if this APIC belongs to a logical destination.
779 *
780 * @returns true if the APIC belongs to the logical
781 * destination, false otherwise.
782 * @param pVCpu The cross context virtual CPU structure.
783 * @param fDest The destination mask.
784 *
785 * @thread Any.
786 */
787static bool apicIsLogicalDest(PVMCPUCC pVCpu, uint32_t fDest)
788{
789 if (XAPIC_IN_X2APIC_MODE(pVCpu))
790 {
791 /*
792 * Flat logical mode is not supported in x2APIC mode.
793 * In clustered logical mode, the 32-bit logical ID in the LDR is interpreted as follows:
794 * - High 16 bits is the cluster ID.
795 * - Low 16 bits: each bit represents a unique APIC within the cluster.
796 */
797 PCX2APICPAGE pX2ApicPage = VMCPU_TO_CX2APICPAGE(pVCpu);
798 uint32_t const u32Ldr = pX2ApicPage->ldr.u32LogicalApicId;
799 if (X2APIC_LDR_GET_CLUSTER_ID(u32Ldr) == (fDest & X2APIC_LDR_CLUSTER_ID))
800 return RT_BOOL(u32Ldr & fDest & X2APIC_LDR_LOGICAL_ID);
801 return false;
802 }
803
804#if XAPIC_HARDWARE_VERSION == XAPIC_HARDWARE_VERSION_P4
805 /*
806 * In both flat and clustered logical mode, a destination mask of all set bits indicates a broadcast.
807 * See AMD spec. 16.6.1 "Receiving System and IPI Interrupts".
808 */
809 Assert(!XAPIC_IN_X2APIC_MODE(pVCpu));
810 if ((fDest & XAPIC_LDR_FLAT_LOGICAL_ID) == XAPIC_LDR_FLAT_LOGICAL_ID)
811 return true;
812
813 PCXAPICPAGE pXApicPage = VMCPU_TO_CXAPICPAGE(pVCpu);
814 XAPICDESTFORMAT enmDestFormat = (XAPICDESTFORMAT)pXApicPage->dfr.u.u4Model;
815 if (enmDestFormat == XAPICDESTFORMAT_FLAT)
816 {
817 /* The destination mask is interpreted as a bitmap of 8 unique logical APIC IDs. */
818 uint8_t const u8Ldr = pXApicPage->ldr.u.u8LogicalApicId;
819 return RT_BOOL(u8Ldr & fDest & XAPIC_LDR_FLAT_LOGICAL_ID);
820 }
821
822 /*
823 * In clustered logical mode, the 8-bit logical ID in the LDR is interpreted as follows:
824 * - High 4 bits is the cluster ID.
825 * - Low 4 bits: each bit represents a unique APIC within the cluster.
826 */
827 Assert(enmDestFormat == XAPICDESTFORMAT_CLUSTER);
828 uint8_t const u8Ldr = pXApicPage->ldr.u.u8LogicalApicId;
829 if (XAPIC_LDR_CLUSTERED_GET_CLUSTER_ID(u8Ldr) == (fDest & XAPIC_LDR_CLUSTERED_CLUSTER_ID))
830 return RT_BOOL(u8Ldr & fDest & XAPIC_LDR_CLUSTERED_LOGICAL_ID);
831 return false;
832#else
833# error "Implement Pentium and P6 family APIC architectures"
834#endif
835}
836
837
838/**
839 * Figures out the set of destination CPUs for a given destination mode, format
840 * and delivery mode setting.
841 *
842 * @param pVM The cross context VM structure.
843 * @param fDestMask The destination mask.
844 * @param fBroadcastMask The broadcast mask.
845 * @param enmDestMode The destination mode.
846 * @param enmDeliveryMode The delivery mode.
847 * @param pDestCpuSet The destination CPU set to update.
848 */
849static void apicGetDestCpuSet(PVMCC pVM, uint32_t fDestMask, uint32_t fBroadcastMask, XAPICDESTMODE enmDestMode,
850 XAPICDELIVERYMODE enmDeliveryMode, PVMCPUSET pDestCpuSet)
851{
852 VMCPUSET_EMPTY(pDestCpuSet);
853
854 /*
855 * Physical destination mode only supports either a broadcast or a single target.
856 * - Broadcast with lowest-priority delivery mode is not supported[1], we deliver it
857 * as a regular broadcast like in fixed delivery mode.
858 * - For a single target, lowest-priority delivery mode makes no sense. We deliver
859 * to the target like in fixed delivery mode.
860 *
861 * [1] See Intel spec. 10.6.2.1 "Physical Destination Mode".
862 */
863 if ( enmDestMode == XAPICDESTMODE_PHYSICAL
864 && enmDeliveryMode == XAPICDELIVERYMODE_LOWEST_PRIO)
865 {
866 AssertMsgFailed(("APIC: Lowest-priority delivery using physical destination mode!"));
867 enmDeliveryMode = XAPICDELIVERYMODE_FIXED;
868 }
869
870 uint32_t const cCpus = pVM->cCpus;
871 if (enmDeliveryMode == XAPICDELIVERYMODE_LOWEST_PRIO)
872 {
873 Assert(enmDestMode == XAPICDESTMODE_LOGICAL);
874#if XAPIC_HARDWARE_VERSION == XAPIC_HARDWARE_VERSION_P4
875 VMCPUID idCpuLowestTpr = NIL_VMCPUID;
876 uint8_t u8LowestTpr = UINT8_C(0xff);
877 for (VMCPUID idCpu = 0; idCpu < cCpus; idCpu++)
878 {
879 PVMCPUCC pVCpuDst = pVM->CTX_SUFF(apCpus)[idCpu];
880 if (apicIsLogicalDest(pVCpuDst, fDestMask))
881 {
882 PCXAPICPAGE pXApicPage = VMCPU_TO_CXAPICPAGE(pVCpuDst);
883 uint8_t const u8Tpr = pXApicPage->tpr.u8Tpr; /* PAV */
884
885 /*
886 * If there is a tie for lowest priority, the local APIC with the highest ID is chosen.
887 * Hence the use of "<=" in the check below.
888 * See AMD spec. 16.6.2 "Lowest Priority Messages and Arbitration".
889 */
890 if (u8Tpr <= u8LowestTpr)
891 {
892 u8LowestTpr = u8Tpr;
893 idCpuLowestTpr = idCpu;
894 }
895 }
896 }
897 if (idCpuLowestTpr != NIL_VMCPUID)
898 VMCPUSET_ADD(pDestCpuSet, idCpuLowestTpr);
899#else
900# error "Implement Pentium and P6 family APIC architectures"
901#endif
902 return;
903 }
904
905 /*
906 * x2APIC:
907 * - In both physical and logical destination mode, a destination mask of 0xffffffff implies a broadcast[1].
908 * xAPIC:
909 * - In physical destination mode, a destination mask of 0xff implies a broadcast[2].
910 * - In both flat and clustered logical mode, a destination mask of 0xff implies a broadcast[3].
911 *
912 * [1] See Intel spec. 10.12.9 "ICR Operation in x2APIC Mode".
913 * [2] See Intel spec. 10.6.2.1 "Physical Destination Mode".
914 * [2] See AMD spec. 16.6.1 "Receiving System and IPI Interrupts".
915 */
916 if ((fDestMask & fBroadcastMask) == fBroadcastMask)
917 {
918 VMCPUSET_FILL(pDestCpuSet);
919 return;
920 }
921
922 if (enmDestMode == XAPICDESTMODE_PHYSICAL)
923 {
924 /* The destination mask is interpreted as the physical APIC ID of a single target. */
925#if 1
926 /* Since our physical APIC ID is read-only to software, set the corresponding bit in the CPU set. */
927 if (RT_LIKELY(fDestMask < cCpus))
928 VMCPUSET_ADD(pDestCpuSet, fDestMask);
929#else
930 /* The physical APIC ID may not match our VCPU ID, search through the list of targets. */
931 for (VMCPUID idCpu = 0; idCpu < cCpus; idCpu++)
932 {
933 PVMCPUCC pVCpuDst = &pVM->aCpus[idCpu];
934 if (XAPIC_IN_X2APIC_MODE(pVCpuDst))
935 {
936 PCX2APICPAGE pX2ApicPage = VMCPU_TO_CX2APICPAGE(pVCpuDst);
937 if (pX2ApicPage->id.u32ApicId == fDestMask)
938 VMCPUSET_ADD(pDestCpuSet, pVCpuDst->idCpu);
939 }
940 else
941 {
942 PCXAPICPAGE pXApicPage = VMCPU_TO_CXAPICPAGE(pVCpuDst);
943 if (pXApicPage->id.u8ApicId == (uint8_t)fDestMask)
944 VMCPUSET_ADD(pDestCpuSet, pVCpuDst->idCpu);
945 }
946 }
947#endif
948 }
949 else
950 {
951 Assert(enmDestMode == XAPICDESTMODE_LOGICAL);
952
953 /* A destination mask of all 0's implies no target APICs (since it's interpreted as a bitmap or partial bitmap). */
954 if (RT_UNLIKELY(!fDestMask))
955 return;
956
957 /* The destination mask is interpreted as a bitmap of software-programmable logical APIC ID of the target APICs. */
958 for (VMCPUID idCpu = 0; idCpu < cCpus; idCpu++)
959 {
960 PVMCPUCC pVCpuDst = pVM->CTX_SUFF(apCpus)[idCpu];
961 if (apicIsLogicalDest(pVCpuDst, fDestMask))
962 VMCPUSET_ADD(pDestCpuSet, pVCpuDst->idCpu);
963 }
964 }
965}
966
967
968/**
969 * Sends an Interprocessor Interrupt (IPI) using values from the Interrupt
970 * Command Register (ICR).
971 *
972 * @returns VBox status code.
973 * @param pVCpu The cross context virtual CPU structure.
974 * @param rcRZ The return code if the operation cannot be
975 * performed in the current context.
976 */
977DECLINLINE(VBOXSTRICTRC) apicSendIpi(PVMCPUCC pVCpu, int rcRZ)
978{
979 VMCPU_ASSERT_EMT(pVCpu);
980
981 PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
982 XAPICDELIVERYMODE const enmDeliveryMode = (XAPICDELIVERYMODE)pXApicPage->icr_lo.u.u3DeliveryMode;
983 XAPICDESTMODE const enmDestMode = (XAPICDESTMODE)pXApicPage->icr_lo.u.u1DestMode;
984 XAPICINITLEVEL const enmInitLevel = (XAPICINITLEVEL)pXApicPage->icr_lo.u.u1Level;
985 XAPICTRIGGERMODE const enmTriggerMode = (XAPICTRIGGERMODE)pXApicPage->icr_lo.u.u1TriggerMode;
986 XAPICDESTSHORTHAND const enmDestShorthand = (XAPICDESTSHORTHAND)pXApicPage->icr_lo.u.u2DestShorthand;
987 uint8_t const uVector = pXApicPage->icr_lo.u.u8Vector;
988
989 PX2APICPAGE pX2ApicPage = VMCPU_TO_X2APICPAGE(pVCpu);
990 uint32_t const fDest = XAPIC_IN_X2APIC_MODE(pVCpu) ? pX2ApicPage->icr_hi.u32IcrHi : pXApicPage->icr_hi.u.u8Dest;
991 Log5(("apicSendIpi: delivery=%u mode=%u init=%u trigger=%u short=%u vector=%#x fDest=%#x\n",
992 enmDeliveryMode, enmDestMode, enmInitLevel, enmTriggerMode, enmDestShorthand, uVector, fDest));
993
994#if XAPIC_HARDWARE_VERSION == XAPIC_HARDWARE_VERSION_P4
995 /*
996 * INIT Level De-assert is not support on Pentium 4 and Xeon processors.
997 * Apparently, this also applies to NMI, SMI, lowest-priority and fixed delivery modes,
998 * see @bugref{8245#c116}.
999 *
1000 * See AMD spec. 16.5 "Interprocessor Interrupts (IPI)" for a table of valid ICR combinations.
1001 */
1002 if ( enmTriggerMode == XAPICTRIGGERMODE_LEVEL
1003 && enmInitLevel == XAPICINITLEVEL_DEASSERT
1004 && ( enmDeliveryMode == XAPICDELIVERYMODE_FIXED
1005 || enmDeliveryMode == XAPICDELIVERYMODE_LOWEST_PRIO
1006 || enmDeliveryMode == XAPICDELIVERYMODE_SMI
1007 || enmDeliveryMode == XAPICDELIVERYMODE_NMI
1008 || enmDeliveryMode == XAPICDELIVERYMODE_INIT))
1009 {
1010 Log2(("APIC%u: %s level de-assert unsupported, ignoring!\n", pVCpu->idCpu, apicGetDeliveryModeName(enmDeliveryMode)));
1011 return VINF_SUCCESS;
1012 }
1013#else
1014# error "Implement Pentium and P6 family APIC architectures"
1015#endif
1016
1017 /*
1018 * The destination and delivery modes are ignored/by-passed when a destination shorthand is specified.
1019 * See Intel spec. 10.6.2.3 "Broadcast/Self Delivery Mode".
1020 */
1021 VMCPUSET DestCpuSet;
1022 switch (enmDestShorthand)
1023 {
1024 case XAPICDESTSHORTHAND_NONE:
1025 {
1026 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
1027 uint32_t const fBroadcastMask = XAPIC_IN_X2APIC_MODE(pVCpu) ? X2APIC_ID_BROADCAST_MASK : XAPIC_ID_BROADCAST_MASK;
1028 apicGetDestCpuSet(pVM, fDest, fBroadcastMask, enmDestMode, enmDeliveryMode, &DestCpuSet);
1029 break;
1030 }
1031
1032 case XAPICDESTSHORTHAND_SELF:
1033 {
1034 VMCPUSET_EMPTY(&DestCpuSet);
1035 VMCPUSET_ADD(&DestCpuSet, pVCpu->idCpu);
1036 break;
1037 }
1038
1039 case XAPIDDESTSHORTHAND_ALL_INCL_SELF:
1040 {
1041 VMCPUSET_FILL(&DestCpuSet);
1042 break;
1043 }
1044
1045 case XAPICDESTSHORTHAND_ALL_EXCL_SELF:
1046 {
1047 VMCPUSET_FILL(&DestCpuSet);
1048 VMCPUSET_DEL(&DestCpuSet, pVCpu->idCpu);
1049 break;
1050 }
1051 }
1052
1053 return apicSendIntr(pVCpu->CTX_SUFF(pVM), pVCpu, uVector, enmTriggerMode, enmDeliveryMode, &DestCpuSet,
1054 NULL /* pfIntrAccepted */, 0 /* uSrcTag */, rcRZ);
1055}
1056
1057
1058/**
1059 * Sets the Interrupt Command Register (ICR) high dword.
1060 *
1061 * @returns Strict VBox status code.
1062 * @param pVCpu The cross context virtual CPU structure.
1063 * @param uIcrHi The ICR high dword.
1064 */
1065static VBOXSTRICTRC apicSetIcrHi(PVMCPUCC pVCpu, uint32_t uIcrHi)
1066{
1067 VMCPU_ASSERT_EMT(pVCpu);
1068 Assert(!XAPIC_IN_X2APIC_MODE(pVCpu));
1069
1070 PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
1071 pXApicPage->icr_hi.all.u32IcrHi = uIcrHi & XAPIC_ICR_HI_DEST;
1072 STAM_COUNTER_INC(&pVCpu->apic.s.StatIcrHiWrite);
1073 Log2(("APIC%u: apicSetIcrHi: uIcrHi=%#RX32\n", pVCpu->idCpu, pXApicPage->icr_hi.all.u32IcrHi));
1074
1075 return VINF_SUCCESS;
1076}
1077
1078
1079/**
1080 * Sets the Interrupt Command Register (ICR) low dword.
1081 *
1082 * @returns Strict VBox status code.
1083 * @param pVCpu The cross context virtual CPU structure.
1084 * @param uIcrLo The ICR low dword.
1085 * @param rcRZ The return code if the operation cannot be performed
1086 * in the current context.
1087 * @param fUpdateStat Whether to update the ICR low write statistics
1088 * counter.
1089 */
1090static VBOXSTRICTRC apicSetIcrLo(PVMCPUCC pVCpu, uint32_t uIcrLo, int rcRZ, bool fUpdateStat)
1091{
1092 VMCPU_ASSERT_EMT(pVCpu);
1093
1094 PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
1095 pXApicPage->icr_lo.all.u32IcrLo = uIcrLo & XAPIC_ICR_LO_WR_VALID;
1096 Log2(("APIC%u: apicSetIcrLo: uIcrLo=%#RX32\n", pVCpu->idCpu, pXApicPage->icr_lo.all.u32IcrLo));
1097
1098 if (fUpdateStat)
1099 STAM_COUNTER_INC(&pVCpu->apic.s.StatIcrLoWrite);
1100 RT_NOREF(fUpdateStat);
1101
1102 return apicSendIpi(pVCpu, rcRZ);
1103}
1104
1105
1106/**
1107 * Sets the Interrupt Command Register (ICR).
1108 *
1109 * @returns Strict VBox status code.
1110 * @param pVCpu The cross context virtual CPU structure.
1111 * @param u64Icr The ICR (High and Low combined).
1112 * @param rcRZ The return code if the operation cannot be performed
1113 * in the current context.
1114 *
1115 * @remarks This function is used by both x2APIC interface and the Hyper-V
1116 * interface, see APICHvSetIcr. The Hyper-V spec isn't clear what
1117 * happens when invalid bits are set. For the time being, it will
1118 * \#GP like a regular x2APIC access.
1119 */
1120static VBOXSTRICTRC apicSetIcr(PVMCPUCC pVCpu, uint64_t u64Icr, int rcRZ)
1121{
1122 VMCPU_ASSERT_EMT(pVCpu);
1123
1124 /* Validate. */
1125 uint32_t const uLo = RT_LO_U32(u64Icr);
1126 if (RT_LIKELY(!(uLo & ~XAPIC_ICR_LO_WR_VALID)))
1127 {
1128 /* Update high dword first, then update the low dword which sends the IPI. */
1129 PX2APICPAGE pX2ApicPage = VMCPU_TO_X2APICPAGE(pVCpu);
1130 pX2ApicPage->icr_hi.u32IcrHi = RT_HI_U32(u64Icr);
1131 STAM_COUNTER_INC(&pVCpu->apic.s.StatIcrFullWrite);
1132 return apicSetIcrLo(pVCpu, uLo, rcRZ, false /* fUpdateStat */);
1133 }
1134 return apicMsrAccessError(pVCpu, MSR_IA32_X2APIC_ICR, APICMSRACCESS_WRITE_RSVD_BITS);
1135}
1136
1137
1138/**
1139 * Sets the Error Status Register (ESR).
1140 *
1141 * @returns VINF_SUCCESS or VERR_CPUM_RAISE_GP_0.
1142 * @param pVCpu The cross context virtual CPU structure.
1143 * @param uEsr The ESR value.
1144 */
1145static int apicSetEsr(PVMCPUCC pVCpu, uint32_t uEsr)
1146{
1147 VMCPU_ASSERT_EMT(pVCpu);
1148
1149 Log2(("APIC%u: apicSetEsr: uEsr=%#RX32\n", pVCpu->idCpu, uEsr));
1150
1151 if ( XAPIC_IN_X2APIC_MODE(pVCpu)
1152 && (uEsr & ~XAPIC_ESR_WO_VALID))
1153 return apicMsrAccessError(pVCpu, MSR_IA32_X2APIC_ESR, APICMSRACCESS_WRITE_RSVD_BITS);
1154
1155 /*
1156 * Writes to the ESR causes the internal state to be updated in the register,
1157 * clearing the original state. See AMD spec. 16.4.6 "APIC Error Interrupts".
1158 */
1159 PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
1160 pXApicPage->esr.all.u32Errors = apicClearAllErrors(pVCpu);
1161 return VINF_SUCCESS;
1162}
1163
1164
1165/**
1166 * Updates the Processor Priority Register (PPR).
1167 *
1168 * @param pVCpu The cross context virtual CPU structure.
1169 */
1170static void apicUpdatePpr(PVMCPUCC pVCpu)
1171{
1172 VMCPU_ASSERT_EMT(pVCpu);
1173
1174 /* See Intel spec 10.8.3.1 "Task and Processor Priorities". */
1175 PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
1176 uint8_t const uIsrv = apicGetHighestSetBitInReg(&pXApicPage->isr, 0 /* rcNotFound */);
1177 uint8_t uPpr;
1178 if (XAPIC_TPR_GET_TP(pXApicPage->tpr.u8Tpr) >= XAPIC_PPR_GET_PP(uIsrv))
1179 uPpr = pXApicPage->tpr.u8Tpr;
1180 else
1181 uPpr = XAPIC_PPR_GET_PP(uIsrv);
1182 pXApicPage->ppr.u8Ppr = uPpr;
1183}
1184
1185
1186/**
1187 * Gets the Processor Priority Register (PPR).
1188 *
1189 * @returns The PPR value.
1190 * @param pVCpu The cross context virtual CPU structure.
1191 */
1192static uint8_t apicGetPpr(PVMCPUCC pVCpu)
1193{
1194 VMCPU_ASSERT_EMT(pVCpu);
1195 STAM_COUNTER_INC(&pVCpu->apic.s.StatTprRead);
1196
1197 /*
1198 * With virtualized APIC registers or with TPR virtualization, the hardware may
1199 * update ISR/TPR transparently. We thus re-calculate the PPR which may be out of sync.
1200 * See Intel spec. 29.2.2 "Virtual-Interrupt Delivery".
1201 *
1202 * In all other instances, whenever the TPR or ISR changes, we need to update the PPR
1203 * as well (e.g. like we do manually in apicR3InitIpi and by calling apicUpdatePpr).
1204 */
1205 PCAPIC pApic = VM_TO_APIC(pVCpu->CTX_SUFF(pVM));
1206 if (pApic->fVirtApicRegsEnabled) /** @todo re-think this */
1207 apicUpdatePpr(pVCpu);
1208 PCXAPICPAGE pXApicPage = VMCPU_TO_CXAPICPAGE(pVCpu);
1209 return pXApicPage->ppr.u8Ppr;
1210}
1211
1212
1213/**
1214 * Sets the Task Priority Register (TPR).
1215 *
1216 * @returns VINF_SUCCESS or VERR_CPUM_RAISE_GP_0.
1217 * @param pVCpu The cross context virtual CPU structure.
1218 * @param uTpr The TPR value.
1219 * @param fForceX2ApicBehaviour Pretend the APIC is in x2APIC mode during
1220 * this write.
1221 */
1222static int apicSetTprEx(PVMCPUCC pVCpu, uint32_t uTpr, bool fForceX2ApicBehaviour)
1223{
1224 VMCPU_ASSERT_EMT(pVCpu);
1225
1226 Log2(("APIC%u: apicSetTprEx: uTpr=%#RX32\n", pVCpu->idCpu, uTpr));
1227 STAM_COUNTER_INC(&pVCpu->apic.s.StatTprWrite);
1228
1229 bool const fX2ApicMode = XAPIC_IN_X2APIC_MODE(pVCpu) || fForceX2ApicBehaviour;
1230 if ( fX2ApicMode
1231 && (uTpr & ~XAPIC_TPR_VALID))
1232 return apicMsrAccessError(pVCpu, MSR_IA32_X2APIC_TPR, APICMSRACCESS_WRITE_RSVD_BITS);
1233
1234 PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
1235 pXApicPage->tpr.u8Tpr = uTpr;
1236 apicUpdatePpr(pVCpu);
1237 apicSignalNextPendingIntr(pVCpu);
1238 return VINF_SUCCESS;
1239}
1240
1241
1242/**
1243 * Sets the End-Of-Interrupt (EOI) register.
1244 *
1245 * @returns Strict VBox status code.
1246 * @param pVCpu The cross context virtual CPU structure.
1247 * @param uEoi The EOI value.
1248 * @param fForceX2ApicBehaviour Pretend the APIC is in x2APIC mode during
1249 * this write.
1250 */
1251static VBOXSTRICTRC apicSetEoi(PVMCPUCC pVCpu, uint32_t uEoi, bool fForceX2ApicBehaviour)
1252{
1253 VMCPU_ASSERT_EMT(pVCpu);
1254
1255 Log2(("APIC%u: apicSetEoi: uEoi=%#RX32\n", pVCpu->idCpu, uEoi));
1256 STAM_COUNTER_INC(&pVCpu->apic.s.StatEoiWrite);
1257
1258 bool const fX2ApicMode = XAPIC_IN_X2APIC_MODE(pVCpu) || fForceX2ApicBehaviour;
1259 if ( fX2ApicMode
1260 && (uEoi & ~XAPIC_EOI_WO_VALID))
1261 return apicMsrAccessError(pVCpu, MSR_IA32_X2APIC_EOI, APICMSRACCESS_WRITE_RSVD_BITS);
1262
1263 PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
1264 int isrv = apicGetHighestSetBitInReg(&pXApicPage->isr, -1 /* rcNotFound */);
1265 if (isrv >= 0)
1266 {
1267 /*
1268 * Broadcast the EOI to the I/O APIC(s).
1269 *
1270 * We'll handle the EOI broadcast first as there is tiny chance we get rescheduled to
1271 * ring-3 due to contention on the I/O APIC lock. This way we don't mess with the rest
1272 * of the APIC state and simply restart the EOI write operation from ring-3.
1273 */
1274 Assert(isrv <= (int)UINT8_MAX);
1275 uint8_t const uVector = isrv;
1276 bool const fLevelTriggered = apicTestVectorInReg(&pXApicPage->tmr, uVector);
1277 if (fLevelTriggered)
1278 {
1279 PDMIoApicBroadcastEoi(pVCpu->CTX_SUFF(pVM), uVector);
1280
1281 /*
1282 * Clear the vector from the TMR.
1283 *
1284 * The broadcast to I/O APIC can re-trigger new interrupts to arrive via the bus. However,
1285 * APICUpdatePendingInterrupts() which updates TMR can only be done from EMT which we
1286 * currently are on, so no possibility of concurrent updates.
1287 */
1288 apicClearVectorInReg(&pXApicPage->tmr, uVector);
1289
1290 /*
1291 * Clear the remote IRR bit for level-triggered, fixed mode LINT0 interrupt.
1292 * The LINT1 pin does not support level-triggered interrupts.
1293 * See Intel spec. 10.5.1 "Local Vector Table".
1294 */
1295 uint32_t const uLvtLint0 = pXApicPage->lvt_lint0.all.u32LvtLint0;
1296 if ( XAPIC_LVT_GET_REMOTE_IRR(uLvtLint0)
1297 && XAPIC_LVT_GET_VECTOR(uLvtLint0) == uVector
1298 && XAPIC_LVT_GET_DELIVERY_MODE(uLvtLint0) == XAPICDELIVERYMODE_FIXED)
1299 {
1300 ASMAtomicAndU32((volatile uint32_t *)&pXApicPage->lvt_lint0.all.u32LvtLint0, ~XAPIC_LVT_REMOTE_IRR);
1301 Log2(("APIC%u: apicSetEoi: Cleared remote-IRR for LINT0. uVector=%#x\n", pVCpu->idCpu, uVector));
1302 }
1303
1304 Log2(("APIC%u: apicSetEoi: Cleared level triggered interrupt from TMR. uVector=%#x\n", pVCpu->idCpu, uVector));
1305 }
1306
1307 /*
1308 * Mark interrupt as serviced, update the PPR and signal pending interrupts.
1309 */
1310 Log2(("APIC%u: apicSetEoi: Clearing interrupt from ISR. uVector=%#x\n", pVCpu->idCpu, uVector));
1311 apicClearVectorInReg(&pXApicPage->isr, uVector);
1312 apicUpdatePpr(pVCpu);
1313 apicSignalNextPendingIntr(pVCpu);
1314 }
1315 else
1316 {
1317#ifdef DEBUG_ramshankar
1318 /** @todo Figure out if this is done intentionally by guests or is a bug
1319 * in our emulation. Happened with Win10 SMP VM during reboot after
1320 * installation of guest additions with 3D support. */
1321 AssertMsgFailed(("APIC%u: apicSetEoi: Failed to find any ISR bit\n", pVCpu->idCpu));
1322#endif
1323 }
1324
1325 return VINF_SUCCESS;
1326}
1327
1328
1329/**
1330 * Sets the Logical Destination Register (LDR).
1331 *
1332 * @returns Strict VBox status code.
1333 * @param pVCpu The cross context virtual CPU structure.
1334 * @param uLdr The LDR value.
1335 *
1336 * @remarks LDR is read-only in x2APIC mode.
1337 */
1338static VBOXSTRICTRC apicSetLdr(PVMCPUCC pVCpu, uint32_t uLdr)
1339{
1340 VMCPU_ASSERT_EMT(pVCpu);
1341 PCAPIC pApic = VM_TO_APIC(pVCpu->CTX_SUFF(pVM));
1342 Assert(!XAPIC_IN_X2APIC_MODE(pVCpu) || pApic->fHyperVCompatMode); RT_NOREF_PV(pApic);
1343
1344 Log2(("APIC%u: apicSetLdr: uLdr=%#RX32\n", pVCpu->idCpu, uLdr));
1345
1346 PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
1347 apicWriteRaw32(pXApicPage, XAPIC_OFF_LDR, uLdr & XAPIC_LDR_VALID);
1348 STAM_COUNTER_INC(&pVCpu->apic.s.StatLdrWrite);
1349 return VINF_SUCCESS;
1350}
1351
1352
1353/**
1354 * Sets the Destination Format Register (DFR).
1355 *
1356 * @returns Strict VBox status code.
1357 * @param pVCpu The cross context virtual CPU structure.
1358 * @param uDfr The DFR value.
1359 *
1360 * @remarks DFR is not available in x2APIC mode.
1361 */
1362static VBOXSTRICTRC apicSetDfr(PVMCPUCC pVCpu, uint32_t uDfr)
1363{
1364 VMCPU_ASSERT_EMT(pVCpu);
1365 Assert(!XAPIC_IN_X2APIC_MODE(pVCpu));
1366
1367 uDfr &= XAPIC_DFR_VALID;
1368 uDfr |= XAPIC_DFR_RSVD_MB1;
1369
1370 Log2(("APIC%u: apicSetDfr: uDfr=%#RX32\n", pVCpu->idCpu, uDfr));
1371
1372 PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
1373 apicWriteRaw32(pXApicPage, XAPIC_OFF_DFR, uDfr);
1374 STAM_COUNTER_INC(&pVCpu->apic.s.StatDfrWrite);
1375 return VINF_SUCCESS;
1376}
1377
1378
1379/**
1380 * Sets the Timer Divide Configuration Register (DCR).
1381 *
1382 * @returns Strict VBox status code.
1383 * @param pVCpu The cross context virtual CPU structure.
1384 * @param uTimerDcr The timer DCR value.
1385 */
1386static VBOXSTRICTRC apicSetTimerDcr(PVMCPUCC pVCpu, uint32_t uTimerDcr)
1387{
1388 VMCPU_ASSERT_EMT(pVCpu);
1389 if ( XAPIC_IN_X2APIC_MODE(pVCpu)
1390 && (uTimerDcr & ~XAPIC_TIMER_DCR_VALID))
1391 return apicMsrAccessError(pVCpu, MSR_IA32_X2APIC_TIMER_DCR, APICMSRACCESS_WRITE_RSVD_BITS);
1392
1393 Log2(("APIC%u: apicSetTimerDcr: uTimerDcr=%#RX32\n", pVCpu->idCpu, uTimerDcr));
1394
1395 PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
1396 apicWriteRaw32(pXApicPage, XAPIC_OFF_TIMER_DCR, uTimerDcr);
1397 STAM_COUNTER_INC(&pVCpu->apic.s.StatDcrWrite);
1398 return VINF_SUCCESS;
1399}
1400
1401
1402/**
1403 * Gets the timer's Current Count Register (CCR).
1404 *
1405 * @returns VBox status code.
1406 * @param pDevIns The device instance.
1407 * @param pVCpu The cross context virtual CPU structure.
1408 * @param rcBusy The busy return code for the timer critical section.
1409 * @param puValue Where to store the LVT timer CCR.
1410 */
1411static VBOXSTRICTRC apicGetTimerCcr(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, int rcBusy, uint32_t *puValue)
1412{
1413 VMCPU_ASSERT_EMT(pVCpu);
1414 Assert(puValue);
1415
1416 PCXAPICPAGE pXApicPage = VMCPU_TO_CXAPICPAGE(pVCpu);
1417 *puValue = 0;
1418
1419 /* In TSC-deadline mode, CCR returns 0, see Intel spec. 10.5.4.1 "TSC-Deadline Mode". */
1420 if (pXApicPage->lvt_timer.u.u2TimerMode == XAPIC_TIMER_MODE_TSC_DEADLINE)
1421 return VINF_SUCCESS;
1422
1423 /* If the initial-count register is 0, CCR returns 0 as it cannot exceed the ICR. */
1424 uint32_t const uInitialCount = pXApicPage->timer_icr.u32InitialCount;
1425 if (!uInitialCount)
1426 return VINF_SUCCESS;
1427
1428 /*
1429 * Reading the virtual-sync clock requires locking its timer because it's not
1430 * a simple atomic operation, see tmVirtualSyncGetEx().
1431 *
1432 * We also need to lock before reading the timer CCR, see apicR3TimerCallback().
1433 */
1434 PCAPICCPU pApicCpu = VMCPU_TO_APICCPU(pVCpu);
1435 TMTIMERHANDLE hTimer = pApicCpu->hTimer;
1436
1437 VBOXSTRICTRC rc = PDMDevHlpTimerLockClock(pDevIns, hTimer, rcBusy);
1438 if (rc == VINF_SUCCESS)
1439 {
1440 /* If the current-count register is 0, it implies the timer expired. */
1441 uint32_t const uCurrentCount = pXApicPage->timer_ccr.u32CurrentCount;
1442 if (uCurrentCount)
1443 {
1444 uint64_t const cTicksElapsed = PDMDevHlpTimerGet(pDevIns, hTimer) - pApicCpu->u64TimerInitial;
1445 PDMDevHlpTimerUnlockClock(pDevIns, hTimer);
1446 uint8_t const uTimerShift = apicGetTimerShift(pXApicPage);
1447 uint64_t const uDelta = cTicksElapsed >> uTimerShift;
1448 if (uInitialCount > uDelta)
1449 *puValue = uInitialCount - uDelta;
1450 }
1451 else
1452 PDMDevHlpTimerUnlockClock(pDevIns, hTimer);
1453 }
1454 return rc;
1455}
1456
1457
1458/**
1459 * Sets the timer's Initial-Count Register (ICR).
1460 *
1461 * @returns Strict VBox status code.
1462 * @param pDevIns The device instance.
1463 * @param pVCpu The cross context virtual CPU structure.
1464 * @param rcBusy The busy return code for the timer critical section.
1465 * @param uInitialCount The timer ICR.
1466 */
1467static VBOXSTRICTRC apicSetTimerIcr(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, int rcBusy, uint32_t uInitialCount)
1468{
1469 VMCPU_ASSERT_EMT(pVCpu);
1470
1471 PAPIC pApic = VM_TO_APIC(pVCpu->CTX_SUFF(pVM));
1472 PAPICCPU pApicCpu = VMCPU_TO_APICCPU(pVCpu);
1473 PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
1474
1475 Log2(("APIC%u: apicSetTimerIcr: uInitialCount=%#RX32\n", pVCpu->idCpu, uInitialCount));
1476 STAM_COUNTER_INC(&pApicCpu->StatTimerIcrWrite);
1477
1478 /* In TSC-deadline mode, timer ICR writes are ignored, see Intel spec. 10.5.4.1 "TSC-Deadline Mode". */
1479 if ( pApic->fSupportsTscDeadline
1480 && pXApicPage->lvt_timer.u.u2TimerMode == XAPIC_TIMER_MODE_TSC_DEADLINE)
1481 return VINF_SUCCESS;
1482
1483 /*
1484 * The timer CCR may be modified by apicR3TimerCallback() in parallel,
1485 * so obtain the lock -before- updating it here to be consistent with the
1486 * timer ICR. We rely on CCR being consistent in apicGetTimerCcr().
1487 */
1488 TMTIMERHANDLE hTimer = pApicCpu->hTimer;
1489 VBOXSTRICTRC rc = PDMDevHlpTimerLockClock(pDevIns, hTimer, rcBusy);
1490 if (rc == VINF_SUCCESS)
1491 {
1492 pXApicPage->timer_icr.u32InitialCount = uInitialCount;
1493 pXApicPage->timer_ccr.u32CurrentCount = uInitialCount;
1494 if (uInitialCount)
1495 apicStartTimer(pVCpu, uInitialCount);
1496 else
1497 apicStopTimer(pVCpu);
1498 PDMDevHlpTimerUnlockClock(pDevIns, hTimer);
1499 }
1500 return rc;
1501}
1502
1503
1504/**
1505 * Sets an LVT entry.
1506 *
1507 * @returns Strict VBox status code.
1508 * @param pVCpu The cross context virtual CPU structure.
1509 * @param offLvt The LVT entry offset in the xAPIC page.
1510 * @param uLvt The LVT value to set.
1511 */
1512static VBOXSTRICTRC apicSetLvtEntry(PVMCPUCC pVCpu, uint16_t offLvt, uint32_t uLvt)
1513{
1514 VMCPU_ASSERT_EMT(pVCpu);
1515
1516#if XAPIC_HARDWARE_VERSION == XAPIC_HARDWARE_VERSION_P4
1517 AssertMsg( offLvt == XAPIC_OFF_LVT_TIMER
1518 || offLvt == XAPIC_OFF_LVT_THERMAL
1519 || offLvt == XAPIC_OFF_LVT_PERF
1520 || offLvt == XAPIC_OFF_LVT_LINT0
1521 || offLvt == XAPIC_OFF_LVT_LINT1
1522 || offLvt == XAPIC_OFF_LVT_ERROR,
1523 ("APIC%u: apicSetLvtEntry: invalid offset, offLvt=%#RX16, uLvt=%#RX32\n", pVCpu->idCpu, offLvt, uLvt));
1524
1525 /*
1526 * If TSC-deadline mode isn't support, ignore the bit in xAPIC mode
1527 * and raise #GP(0) in x2APIC mode.
1528 */
1529 PCAPIC pApic = VM_TO_APIC(pVCpu->CTX_SUFF(pVM));
1530 if (offLvt == XAPIC_OFF_LVT_TIMER)
1531 {
1532 STAM_COUNTER_INC(&pVCpu->apic.s.StatLvtTimerWrite);
1533 if ( !pApic->fSupportsTscDeadline
1534 && (uLvt & XAPIC_LVT_TIMER_TSCDEADLINE))
1535 {
1536 if (XAPIC_IN_X2APIC_MODE(pVCpu))
1537 return apicMsrAccessError(pVCpu, XAPIC_GET_X2APIC_MSR(offLvt), APICMSRACCESS_WRITE_RSVD_BITS);
1538 uLvt &= ~XAPIC_LVT_TIMER_TSCDEADLINE;
1539 /** @todo TSC-deadline timer mode transition */
1540 }
1541 }
1542
1543 /*
1544 * Validate rest of the LVT bits.
1545 */
1546 uint16_t const idxLvt = (offLvt - XAPIC_OFF_LVT_START) >> 4;
1547 AssertReturn(idxLvt < RT_ELEMENTS(g_au32LvtValidMasks), VERR_OUT_OF_RANGE);
1548
1549 /*
1550 * For x2APIC, disallow setting of invalid/reserved bits.
1551 * For xAPIC, mask out invalid/reserved bits (i.e. ignore them).
1552 */
1553 if ( XAPIC_IN_X2APIC_MODE(pVCpu)
1554 && (uLvt & ~g_au32LvtValidMasks[idxLvt]))
1555 return apicMsrAccessError(pVCpu, XAPIC_GET_X2APIC_MSR(offLvt), APICMSRACCESS_WRITE_RSVD_BITS);
1556
1557 uLvt &= g_au32LvtValidMasks[idxLvt];
1558
1559 /*
1560 * In the software-disabled state, LVT mask-bit must remain set and attempts to clear the mask
1561 * bit must be ignored. See Intel spec. 10.4.7.2 "Local APIC State After It Has Been Software Disabled".
1562 */
1563 PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
1564 if (!pXApicPage->svr.u.fApicSoftwareEnable)
1565 uLvt |= XAPIC_LVT_MASK;
1566
1567 /*
1568 * It is unclear whether we should signal a 'send illegal vector' error here and ignore updating
1569 * the LVT entry when the delivery mode is 'fixed'[1] or update it in addition to signalling the
1570 * error or not signal the error at all. For now, we'll allow setting illegal vectors into the LVT
1571 * but set the 'send illegal vector' error here. The 'receive illegal vector' error will be set if
1572 * the interrupt for the vector happens to be generated, see apicPostInterrupt().
1573 *
1574 * [1] See Intel spec. 10.5.2 "Valid Interrupt Vectors".
1575 */
1576 if (RT_UNLIKELY( XAPIC_LVT_GET_VECTOR(uLvt) <= XAPIC_ILLEGAL_VECTOR_END
1577 && XAPIC_LVT_GET_DELIVERY_MODE(uLvt) == XAPICDELIVERYMODE_FIXED))
1578 apicSetError(pVCpu, XAPIC_ESR_SEND_ILLEGAL_VECTOR);
1579
1580 Log2(("APIC%u: apicSetLvtEntry: offLvt=%#RX16 uLvt=%#RX32\n", pVCpu->idCpu, offLvt, uLvt));
1581
1582 apicWriteRaw32(pXApicPage, offLvt, uLvt);
1583 return VINF_SUCCESS;
1584#else
1585# error "Implement Pentium and P6 family APIC architectures"
1586#endif /* XAPIC_HARDWARE_VERSION == XAPIC_HARDWARE_VERSION_P4 */
1587}
1588
1589
1590#if 0
1591/**
1592 * Sets an LVT entry in the extended LVT range.
1593 *
1594 * @returns VBox status code.
1595 * @param pVCpu The cross context virtual CPU structure.
1596 * @param offLvt The LVT entry offset in the xAPIC page.
1597 * @param uValue The LVT value to set.
1598 */
1599static int apicSetLvtExtEntry(PVMCPUCC pVCpu, uint16_t offLvt, uint32_t uLvt)
1600{
1601 VMCPU_ASSERT_EMT(pVCpu);
1602 AssertMsg(offLvt == XAPIC_OFF_CMCI, ("APIC%u: apicSetLvt1Entry: invalid offset %#RX16\n", pVCpu->idCpu, offLvt));
1603
1604 /** @todo support CMCI. */
1605 return VERR_NOT_IMPLEMENTED;
1606}
1607#endif
1608
1609
1610/**
1611 * Hints TM about the APIC timer frequency.
1612 *
1613 * @param pDevIns The device instance.
1614 * @param pApicCpu The APIC CPU state.
1615 * @param uInitialCount The new initial count.
1616 * @param uTimerShift The new timer shift.
1617 * @thread Any.
1618 */
1619void apicHintTimerFreq(PPDMDEVINS pDevIns, PAPICCPU pApicCpu, uint32_t uInitialCount, uint8_t uTimerShift)
1620{
1621 Assert(pApicCpu);
1622
1623 if ( pApicCpu->uHintedTimerInitialCount != uInitialCount
1624 || pApicCpu->uHintedTimerShift != uTimerShift)
1625 {
1626 uint32_t uHz;
1627 if (uInitialCount)
1628 {
1629 uint64_t cTicksPerPeriod = (uint64_t)uInitialCount << uTimerShift;
1630 uHz = PDMDevHlpTimerGetFreq(pDevIns, pApicCpu->hTimer) / cTicksPerPeriod;
1631 }
1632 else
1633 uHz = 0;
1634
1635 PDMDevHlpTimerSetFrequencyHint(pDevIns, pApicCpu->hTimer, uHz);
1636 pApicCpu->uHintedTimerInitialCount = uInitialCount;
1637 pApicCpu->uHintedTimerShift = uTimerShift;
1638 }
1639}
1640
1641
1642/**
1643 * Gets the Interrupt Command Register (ICR), without performing any interface
1644 * checks.
1645 *
1646 * @returns The ICR value.
1647 * @param pVCpu The cross context virtual CPU structure.
1648 */
1649DECLINLINE(uint64_t) apicGetIcrNoCheck(PVMCPUCC pVCpu)
1650{
1651 PCX2APICPAGE pX2ApicPage = VMCPU_TO_CX2APICPAGE(pVCpu);
1652 uint64_t const uHi = pX2ApicPage->icr_hi.u32IcrHi;
1653 uint64_t const uLo = pX2ApicPage->icr_lo.all.u32IcrLo;
1654 uint64_t const uIcr = RT_MAKE_U64(uLo, uHi);
1655 return uIcr;
1656}
1657
1658
1659/**
1660 * Reads an APIC register.
1661 *
1662 * @returns VBox status code.
1663 * @param pDevIns The device instance.
1664 * @param pVCpu The cross context virtual CPU structure.
1665 * @param offReg The offset of the register being read.
1666 * @param puValue Where to store the register value.
1667 */
1668DECLINLINE(VBOXSTRICTRC) apicReadRegister(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t *puValue)
1669{
1670 VMCPU_ASSERT_EMT(pVCpu);
1671 Assert(offReg <= XAPIC_OFF_MAX_VALID);
1672
1673 PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
1674 uint32_t uValue = 0;
1675 VBOXSTRICTRC rc = VINF_SUCCESS;
1676 switch (offReg)
1677 {
1678 case XAPIC_OFF_ID:
1679 case XAPIC_OFF_VERSION:
1680 case XAPIC_OFF_TPR:
1681 case XAPIC_OFF_EOI:
1682 case XAPIC_OFF_RRD:
1683 case XAPIC_OFF_LDR:
1684 case XAPIC_OFF_DFR:
1685 case XAPIC_OFF_SVR:
1686 case XAPIC_OFF_ISR0: case XAPIC_OFF_ISR1: case XAPIC_OFF_ISR2: case XAPIC_OFF_ISR3:
1687 case XAPIC_OFF_ISR4: case XAPIC_OFF_ISR5: case XAPIC_OFF_ISR6: case XAPIC_OFF_ISR7:
1688 case XAPIC_OFF_TMR0: case XAPIC_OFF_TMR1: case XAPIC_OFF_TMR2: case XAPIC_OFF_TMR3:
1689 case XAPIC_OFF_TMR4: case XAPIC_OFF_TMR5: case XAPIC_OFF_TMR6: case XAPIC_OFF_TMR7:
1690 case XAPIC_OFF_IRR0: case XAPIC_OFF_IRR1: case XAPIC_OFF_IRR2: case XAPIC_OFF_IRR3:
1691 case XAPIC_OFF_IRR4: case XAPIC_OFF_IRR5: case XAPIC_OFF_IRR6: case XAPIC_OFF_IRR7:
1692 case XAPIC_OFF_ESR:
1693 case XAPIC_OFF_ICR_LO:
1694 case XAPIC_OFF_ICR_HI:
1695 case XAPIC_OFF_LVT_TIMER:
1696#if XAPIC_HARDWARE_VERSION == XAPIC_HARDWARE_VERSION_P4
1697 case XAPIC_OFF_LVT_THERMAL:
1698#endif
1699 case XAPIC_OFF_LVT_PERF:
1700 case XAPIC_OFF_LVT_LINT0:
1701 case XAPIC_OFF_LVT_LINT1:
1702 case XAPIC_OFF_LVT_ERROR:
1703 case XAPIC_OFF_TIMER_ICR:
1704 case XAPIC_OFF_TIMER_DCR:
1705 {
1706 Assert( !XAPIC_IN_X2APIC_MODE(pVCpu)
1707 || ( offReg != XAPIC_OFF_DFR
1708 && offReg != XAPIC_OFF_ICR_HI
1709 && offReg != XAPIC_OFF_EOI));
1710 uValue = apicReadRaw32(pXApicPage, offReg);
1711 Log2(("APIC%u: apicReadRegister: offReg=%#x uValue=%#x\n", pVCpu->idCpu, offReg, uValue));
1712 break;
1713 }
1714
1715 case XAPIC_OFF_PPR:
1716 {
1717 uValue = apicGetPpr(pVCpu);
1718 break;
1719 }
1720
1721 case XAPIC_OFF_TIMER_CCR:
1722 {
1723 Assert(!XAPIC_IN_X2APIC_MODE(pVCpu));
1724 rc = apicGetTimerCcr(pDevIns, pVCpu, VINF_IOM_R3_MMIO_READ, &uValue);
1725 break;
1726 }
1727
1728 case XAPIC_OFF_APR:
1729 {
1730#if XAPIC_HARDWARE_VERSION == XAPIC_HARDWARE_VERSION_P4
1731 /* Unsupported on Pentium 4 and Xeon CPUs, invalid in x2APIC mode. */
1732 Assert(!XAPIC_IN_X2APIC_MODE(pVCpu));
1733#else
1734# error "Implement Pentium and P6 family APIC architectures"
1735#endif
1736 break;
1737 }
1738
1739 default:
1740 {
1741 Assert(!XAPIC_IN_X2APIC_MODE(pVCpu));
1742 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "VCPU[%u]: offReg=%#RX16\n", pVCpu->idCpu, offReg);
1743 apicSetError(pVCpu, XAPIC_ESR_ILLEGAL_REG_ADDRESS);
1744 break;
1745 }
1746 }
1747
1748 *puValue = uValue;
1749 return rc;
1750}
1751
1752
1753/**
1754 * Writes an APIC register.
1755 *
1756 * @returns Strict VBox status code.
1757 * @param pDevIns The device instance.
1758 * @param pVCpu The cross context virtual CPU structure.
1759 * @param offReg The offset of the register being written.
1760 * @param uValue The register value.
1761 */
1762DECLINLINE(VBOXSTRICTRC) apicWriteRegister(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t uValue)
1763{
1764 VMCPU_ASSERT_EMT(pVCpu);
1765 Assert(offReg <= XAPIC_OFF_MAX_VALID);
1766 Assert(!XAPIC_IN_X2APIC_MODE(pVCpu));
1767
1768 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
1769 switch (offReg)
1770 {
1771 case XAPIC_OFF_TPR:
1772 {
1773 rcStrict = apicSetTprEx(pVCpu, uValue, false /* fForceX2ApicBehaviour */);
1774 break;
1775 }
1776
1777 case XAPIC_OFF_LVT_TIMER:
1778#if XAPIC_HARDWARE_VERSION == XAPIC_HARDWARE_VERSION_P4
1779 case XAPIC_OFF_LVT_THERMAL:
1780#endif
1781 case XAPIC_OFF_LVT_PERF:
1782 case XAPIC_OFF_LVT_LINT0:
1783 case XAPIC_OFF_LVT_LINT1:
1784 case XAPIC_OFF_LVT_ERROR:
1785 {
1786 rcStrict = apicSetLvtEntry(pVCpu, offReg, uValue);
1787 break;
1788 }
1789
1790 case XAPIC_OFF_TIMER_ICR:
1791 {
1792 rcStrict = apicSetTimerIcr(pDevIns, pVCpu, VINF_IOM_R3_MMIO_WRITE, uValue);
1793 break;
1794 }
1795
1796 case XAPIC_OFF_EOI:
1797 {
1798 rcStrict = apicSetEoi(pVCpu, uValue, false /* fForceX2ApicBehaviour */);
1799 break;
1800 }
1801
1802 case XAPIC_OFF_LDR:
1803 {
1804 rcStrict = apicSetLdr(pVCpu, uValue);
1805 break;
1806 }
1807
1808 case XAPIC_OFF_DFR:
1809 {
1810 rcStrict = apicSetDfr(pVCpu, uValue);
1811 break;
1812 }
1813
1814 case XAPIC_OFF_SVR:
1815 {
1816 rcStrict = apicSetSvr(pVCpu, uValue);
1817 break;
1818 }
1819
1820 case XAPIC_OFF_ICR_LO:
1821 {
1822 rcStrict = apicSetIcrLo(pVCpu, uValue, VINF_IOM_R3_MMIO_WRITE, true /* fUpdateStat */);
1823 break;
1824 }
1825
1826 case XAPIC_OFF_ICR_HI:
1827 {
1828 rcStrict = apicSetIcrHi(pVCpu, uValue);
1829 break;
1830 }
1831
1832 case XAPIC_OFF_TIMER_DCR:
1833 {
1834 rcStrict = apicSetTimerDcr(pVCpu, uValue);
1835 break;
1836 }
1837
1838 case XAPIC_OFF_ESR:
1839 {
1840 rcStrict = apicSetEsr(pVCpu, uValue);
1841 break;
1842 }
1843
1844 case XAPIC_OFF_APR:
1845 case XAPIC_OFF_RRD:
1846 {
1847#if XAPIC_HARDWARE_VERSION == XAPIC_HARDWARE_VERSION_P4
1848 /* Unsupported on Pentium 4 and Xeon CPUs but writes do -not- set an illegal register access error. */
1849#else
1850# error "Implement Pentium and P6 family APIC architectures"
1851#endif
1852 break;
1853 }
1854
1855 /* Read-only, write ignored: */
1856 case XAPIC_OFF_VERSION:
1857 case XAPIC_OFF_ID:
1858 break;
1859
1860 /* Unavailable/reserved in xAPIC mode: */
1861 case X2APIC_OFF_SELF_IPI:
1862 /* Read-only registers: */
1863 case XAPIC_OFF_PPR:
1864 case XAPIC_OFF_ISR0: case XAPIC_OFF_ISR1: case XAPIC_OFF_ISR2: case XAPIC_OFF_ISR3:
1865 case XAPIC_OFF_ISR4: case XAPIC_OFF_ISR5: case XAPIC_OFF_ISR6: case XAPIC_OFF_ISR7:
1866 case XAPIC_OFF_TMR0: case XAPIC_OFF_TMR1: case XAPIC_OFF_TMR2: case XAPIC_OFF_TMR3:
1867 case XAPIC_OFF_TMR4: case XAPIC_OFF_TMR5: case XAPIC_OFF_TMR6: case XAPIC_OFF_TMR7:
1868 case XAPIC_OFF_IRR0: case XAPIC_OFF_IRR1: case XAPIC_OFF_IRR2: case XAPIC_OFF_IRR3:
1869 case XAPIC_OFF_IRR4: case XAPIC_OFF_IRR5: case XAPIC_OFF_IRR6: case XAPIC_OFF_IRR7:
1870 case XAPIC_OFF_TIMER_CCR:
1871 default:
1872 {
1873 rcStrict = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "APIC%u: offReg=%#RX16\n", pVCpu->idCpu, offReg);
1874 apicSetError(pVCpu, XAPIC_ESR_ILLEGAL_REG_ADDRESS);
1875 break;
1876 }
1877 }
1878
1879 return rcStrict;
1880}
1881
1882
1883/**
1884 * Reads an APIC MSR.
1885 *
1886 * @returns Strict VBox status code.
1887 * @param pVCpu The cross context virtual CPU structure.
1888 * @param u32Reg The MSR being read.
1889 * @param pu64Value Where to store the read value.
1890 */
1891VMM_INT_DECL(VBOXSTRICTRC) APICReadMsr(PVMCPUCC pVCpu, uint32_t u32Reg, uint64_t *pu64Value)
1892{
1893 /*
1894 * Validate.
1895 */
1896 VMCPU_ASSERT_EMT(pVCpu);
1897 Assert(u32Reg >= MSR_IA32_X2APIC_ID && u32Reg <= MSR_IA32_X2APIC_SELF_IPI);
1898 Assert(pu64Value);
1899
1900 /*
1901 * Is the APIC enabled?
1902 */
1903 PCAPIC pApic = VM_TO_APIC(pVCpu->CTX_SUFF(pVM));
1904 if (APICIsEnabled(pVCpu))
1905 { /* likely */ }
1906 else
1907 return apicMsrAccessError(pVCpu, u32Reg, pApic->enmMaxMode == PDMAPICMODE_NONE ?
1908 APICMSRACCESS_READ_DISALLOWED_CONFIG : APICMSRACCESS_READ_RSVD_OR_UNKNOWN);
1909
1910#ifndef IN_RING3
1911 if (pApic->CTXALLMID(f,Enabled))
1912 { /* likely */}
1913 else
1914 return VINF_CPUM_R3_MSR_READ;
1915#endif
1916
1917 STAM_COUNTER_INC(&pVCpu->apic.s.CTX_SUFF_Z(StatMsrRead));
1918
1919 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
1920 if (RT_LIKELY( XAPIC_IN_X2APIC_MODE(pVCpu)
1921 || pApic->fHyperVCompatMode))
1922 {
1923 switch (u32Reg)
1924 {
1925 /* Special handling for x2APIC: */
1926 case MSR_IA32_X2APIC_ICR:
1927 {
1928 *pu64Value = apicGetIcrNoCheck(pVCpu);
1929 break;
1930 }
1931
1932 /* Special handling, compatible with xAPIC: */
1933 case MSR_IA32_X2APIC_TIMER_CCR:
1934 {
1935 uint32_t uValue;
1936 rcStrict = apicGetTimerCcr(VMCPU_TO_DEVINS(pVCpu), pVCpu, VINF_CPUM_R3_MSR_READ, &uValue);
1937 *pu64Value = uValue;
1938 break;
1939 }
1940
1941 /* Special handling, compatible with xAPIC: */
1942 case MSR_IA32_X2APIC_PPR:
1943 {
1944 *pu64Value = apicGetPpr(pVCpu);
1945 break;
1946 }
1947
1948 /* Raw read, compatible with xAPIC: */
1949 case MSR_IA32_X2APIC_ID:
1950 {
1951 STAM_COUNTER_INC(&pVCpu->apic.s.StatIdMsrRead);
1952 /* Horrible macOS hack (sample rdmsr addres: 0008:ffffff801686f21a). */
1953 if ( !pApic->fMacOSWorkaround
1954 || pVCpu->cpum.GstCtx.cs.Sel != 8
1955 || pVCpu->cpum.GstCtx.rip < UINT64_C(0xffffff8000000000))
1956 { /* likely */ }
1957 else
1958 {
1959 PCX2APICPAGE pX2ApicPage = VMCPU_TO_CX2APICPAGE(pVCpu);
1960 uint32_t const idApic = pX2ApicPage->id.u32ApicId;
1961 *pu64Value = (idApic << 24) | idApic;
1962 Log(("APIC: Applying macOS hack to MSR_IA32_X2APIC_ID: %#RX64\n", *pu64Value));
1963 break;
1964 }
1965 RT_FALL_THRU();
1966 }
1967 case MSR_IA32_X2APIC_VERSION:
1968 case MSR_IA32_X2APIC_TPR:
1969 case MSR_IA32_X2APIC_LDR:
1970 case MSR_IA32_X2APIC_SVR:
1971 case MSR_IA32_X2APIC_ISR0: case MSR_IA32_X2APIC_ISR1: case MSR_IA32_X2APIC_ISR2: case MSR_IA32_X2APIC_ISR3:
1972 case MSR_IA32_X2APIC_ISR4: case MSR_IA32_X2APIC_ISR5: case MSR_IA32_X2APIC_ISR6: case MSR_IA32_X2APIC_ISR7:
1973 case MSR_IA32_X2APIC_TMR0: case MSR_IA32_X2APIC_TMR1: case MSR_IA32_X2APIC_TMR2: case MSR_IA32_X2APIC_TMR3:
1974 case MSR_IA32_X2APIC_TMR4: case MSR_IA32_X2APIC_TMR5: case MSR_IA32_X2APIC_TMR6: case MSR_IA32_X2APIC_TMR7:
1975 case MSR_IA32_X2APIC_IRR0: case MSR_IA32_X2APIC_IRR1: case MSR_IA32_X2APIC_IRR2: case MSR_IA32_X2APIC_IRR3:
1976 case MSR_IA32_X2APIC_IRR4: case MSR_IA32_X2APIC_IRR5: case MSR_IA32_X2APIC_IRR6: case MSR_IA32_X2APIC_IRR7:
1977 case MSR_IA32_X2APIC_ESR:
1978 case MSR_IA32_X2APIC_LVT_TIMER:
1979 case MSR_IA32_X2APIC_LVT_THERMAL:
1980 case MSR_IA32_X2APIC_LVT_PERF:
1981 case MSR_IA32_X2APIC_LVT_LINT0:
1982 case MSR_IA32_X2APIC_LVT_LINT1:
1983 case MSR_IA32_X2APIC_LVT_ERROR:
1984 case MSR_IA32_X2APIC_TIMER_ICR:
1985 case MSR_IA32_X2APIC_TIMER_DCR:
1986 {
1987 PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
1988 uint16_t const offReg = X2APIC_GET_XAPIC_OFF(u32Reg);
1989 *pu64Value = apicReadRaw32(pXApicPage, offReg);
1990 break;
1991 }
1992
1993 /* Write-only MSRs: */
1994 case MSR_IA32_X2APIC_SELF_IPI:
1995 case MSR_IA32_X2APIC_EOI:
1996 {
1997 rcStrict = apicMsrAccessError(pVCpu, u32Reg, APICMSRACCESS_READ_WRITE_ONLY);
1998 break;
1999 }
2000
2001 /*
2002 * Windows guest using Hyper-V x2APIC MSR compatibility mode tries to read the "high"
2003 * LDR bits, which is quite absurd (as it's a 32-bit register) using this invalid MSR
2004 * index (0x80E), see @bugref{8382#c175}.
2005 */
2006 case MSR_IA32_X2APIC_LDR + 1:
2007 {
2008 if (pApic->fHyperVCompatMode)
2009 *pu64Value = 0;
2010 else
2011 rcStrict = apicMsrAccessError(pVCpu, u32Reg, APICMSRACCESS_READ_RSVD_OR_UNKNOWN);
2012 break;
2013 }
2014
2015 /* Reserved MSRs: */
2016 case MSR_IA32_X2APIC_LVT_CMCI:
2017 default:
2018 {
2019 rcStrict = apicMsrAccessError(pVCpu, u32Reg, APICMSRACCESS_READ_RSVD_OR_UNKNOWN);
2020 break;
2021 }
2022 }
2023 }
2024 else
2025 rcStrict = apicMsrAccessError(pVCpu, u32Reg, APICMSRACCESS_INVALID_READ_MODE);
2026
2027 return rcStrict;
2028}
2029
2030
2031/**
2032 * Writes an APIC MSR.
2033 *
2034 * @returns Strict VBox status code.
2035 * @param pVCpu The cross context virtual CPU structure.
2036 * @param u32Reg The MSR being written.
2037 * @param u64Value The value to write.
2038 */
2039VMM_INT_DECL(VBOXSTRICTRC) APICWriteMsr(PVMCPUCC pVCpu, uint32_t u32Reg, uint64_t u64Value)
2040{
2041 /*
2042 * Validate.
2043 */
2044 VMCPU_ASSERT_EMT(pVCpu);
2045 Assert(u32Reg >= MSR_IA32_X2APIC_ID && u32Reg <= MSR_IA32_X2APIC_SELF_IPI);
2046
2047 /*
2048 * Is the APIC enabled?
2049 */
2050 PCAPIC pApic = VM_TO_APIC(pVCpu->CTX_SUFF(pVM));
2051 if (APICIsEnabled(pVCpu))
2052 { /* likely */ }
2053 else
2054 return apicMsrAccessError(pVCpu, u32Reg, pApic->enmMaxMode == PDMAPICMODE_NONE ?
2055 APICMSRACCESS_WRITE_DISALLOWED_CONFIG : APICMSRACCESS_WRITE_RSVD_OR_UNKNOWN);
2056
2057#ifndef IN_RING3
2058 if (pApic->CTXALLMID(f,Enabled))
2059 { /* likely */ }
2060 else
2061 return VINF_CPUM_R3_MSR_WRITE;
2062#endif
2063
2064 STAM_COUNTER_INC(&pVCpu->apic.s.CTX_SUFF_Z(StatMsrWrite));
2065
2066 /*
2067 * In x2APIC mode, we need to raise #GP(0) for writes to reserved bits, unlike MMIO
2068 * accesses where they are ignored. Hence, we need to validate each register before
2069 * invoking the generic/xAPIC write functions.
2070 *
2071 * Bits 63:32 of all registers except the ICR are reserved, we'll handle this common
2072 * case first and handle validating the remaining bits on a per-register basis.
2073 * See Intel spec. 10.12.1.2 "x2APIC Register Address Space".
2074 */
2075 if ( u32Reg != MSR_IA32_X2APIC_ICR
2076 && RT_HI_U32(u64Value))
2077 return apicMsrAccessError(pVCpu, u32Reg, APICMSRACCESS_WRITE_RSVD_BITS);
2078
2079 uint32_t u32Value = RT_LO_U32(u64Value);
2080 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
2081 if (RT_LIKELY( XAPIC_IN_X2APIC_MODE(pVCpu)
2082 || pApic->fHyperVCompatMode))
2083 {
2084 switch (u32Reg)
2085 {
2086 case MSR_IA32_X2APIC_TPR:
2087 {
2088 rcStrict = apicSetTprEx(pVCpu, u32Value, false /* fForceX2ApicBehaviour */);
2089 break;
2090 }
2091
2092 case MSR_IA32_X2APIC_ICR:
2093 {
2094 rcStrict = apicSetIcr(pVCpu, u64Value, VINF_CPUM_R3_MSR_WRITE);
2095 break;
2096 }
2097
2098 case MSR_IA32_X2APIC_SVR:
2099 {
2100 rcStrict = apicSetSvr(pVCpu, u32Value);
2101 break;
2102 }
2103
2104 case MSR_IA32_X2APIC_ESR:
2105 {
2106 rcStrict = apicSetEsr(pVCpu, u32Value);
2107 break;
2108 }
2109
2110 case MSR_IA32_X2APIC_TIMER_DCR:
2111 {
2112 rcStrict = apicSetTimerDcr(pVCpu, u32Value);
2113 break;
2114 }
2115
2116 case MSR_IA32_X2APIC_LVT_TIMER:
2117 case MSR_IA32_X2APIC_LVT_THERMAL:
2118 case MSR_IA32_X2APIC_LVT_PERF:
2119 case MSR_IA32_X2APIC_LVT_LINT0:
2120 case MSR_IA32_X2APIC_LVT_LINT1:
2121 case MSR_IA32_X2APIC_LVT_ERROR:
2122 {
2123 rcStrict = apicSetLvtEntry(pVCpu, X2APIC_GET_XAPIC_OFF(u32Reg), u32Value);
2124 break;
2125 }
2126
2127 case MSR_IA32_X2APIC_TIMER_ICR:
2128 {
2129 rcStrict = apicSetTimerIcr(VMCPU_TO_DEVINS(pVCpu), pVCpu, VINF_CPUM_R3_MSR_WRITE, u32Value);
2130 break;
2131 }
2132
2133 /* Write-only MSRs: */
2134 case MSR_IA32_X2APIC_SELF_IPI:
2135 {
2136 uint8_t const uVector = XAPIC_SELF_IPI_GET_VECTOR(u32Value);
2137 apicPostInterrupt(pVCpu, uVector, XAPICTRIGGERMODE_EDGE, 0 /* uSrcTag */);
2138 rcStrict = VINF_SUCCESS;
2139 break;
2140 }
2141
2142 case MSR_IA32_X2APIC_EOI:
2143 {
2144 rcStrict = apicSetEoi(pVCpu, u32Value, false /* fForceX2ApicBehaviour */);
2145 break;
2146 }
2147
2148 /*
2149 * Windows guest using Hyper-V x2APIC MSR compatibility mode tries to write the "high"
2150 * LDR bits, which is quite absurd (as it's a 32-bit register) using this invalid MSR
2151 * index (0x80E). The write value was 0xffffffff on a Windows 8.1 64-bit guest. We can
2152 * safely ignore this nonsense, See @bugref{8382#c7}.
2153 */
2154 case MSR_IA32_X2APIC_LDR + 1:
2155 {
2156 if (pApic->fHyperVCompatMode)
2157 rcStrict = VINF_SUCCESS;
2158 else
2159 rcStrict = apicMsrAccessError(pVCpu, u32Reg, APICMSRACCESS_WRITE_RSVD_OR_UNKNOWN);
2160 break;
2161 }
2162
2163 /* Special-treament (read-only normally, but not with Hyper-V) */
2164 case MSR_IA32_X2APIC_LDR:
2165 {
2166 if (pApic->fHyperVCompatMode)
2167 {
2168 rcStrict = apicSetLdr(pVCpu, u32Value);
2169 break;
2170 }
2171 }
2172 RT_FALL_THRU();
2173 /* Read-only MSRs: */
2174 case MSR_IA32_X2APIC_ID:
2175 case MSR_IA32_X2APIC_VERSION:
2176 case MSR_IA32_X2APIC_PPR:
2177 case MSR_IA32_X2APIC_ISR0: case MSR_IA32_X2APIC_ISR1: case MSR_IA32_X2APIC_ISR2: case MSR_IA32_X2APIC_ISR3:
2178 case MSR_IA32_X2APIC_ISR4: case MSR_IA32_X2APIC_ISR5: case MSR_IA32_X2APIC_ISR6: case MSR_IA32_X2APIC_ISR7:
2179 case MSR_IA32_X2APIC_TMR0: case MSR_IA32_X2APIC_TMR1: case MSR_IA32_X2APIC_TMR2: case MSR_IA32_X2APIC_TMR3:
2180 case MSR_IA32_X2APIC_TMR4: case MSR_IA32_X2APIC_TMR5: case MSR_IA32_X2APIC_TMR6: case MSR_IA32_X2APIC_TMR7:
2181 case MSR_IA32_X2APIC_IRR0: case MSR_IA32_X2APIC_IRR1: case MSR_IA32_X2APIC_IRR2: case MSR_IA32_X2APIC_IRR3:
2182 case MSR_IA32_X2APIC_IRR4: case MSR_IA32_X2APIC_IRR5: case MSR_IA32_X2APIC_IRR6: case MSR_IA32_X2APIC_IRR7:
2183 case MSR_IA32_X2APIC_TIMER_CCR:
2184 {
2185 rcStrict = apicMsrAccessError(pVCpu, u32Reg, APICMSRACCESS_WRITE_READ_ONLY);
2186 break;
2187 }
2188
2189 /* Reserved MSRs: */
2190 case MSR_IA32_X2APIC_LVT_CMCI:
2191 default:
2192 {
2193 rcStrict = apicMsrAccessError(pVCpu, u32Reg, APICMSRACCESS_WRITE_RSVD_OR_UNKNOWN);
2194 break;
2195 }
2196 }
2197 }
2198 else
2199 rcStrict = apicMsrAccessError(pVCpu, u32Reg, APICMSRACCESS_INVALID_WRITE_MODE);
2200
2201 return rcStrict;
2202}
2203
2204
2205/**
2206 * Resets the APIC base MSR.
2207 *
2208 * @param pVCpu The cross context virtual CPU structure.
2209 */
2210static void apicResetBaseMsr(PVMCPUCC pVCpu)
2211{
2212 /*
2213 * Initialize the APIC base MSR. The APIC enable-bit is set upon power-up or reset[1].
2214 *
2215 * A Reset (in xAPIC and x2APIC mode) brings up the local APIC in xAPIC mode.
2216 * An INIT IPI does -not- cause a transition between xAPIC and x2APIC mode[2].
2217 *
2218 * [1] See AMD spec. 14.1.3 "Processor Initialization State"
2219 * [2] See Intel spec. 10.12.5.1 "x2APIC States".
2220 */
2221 VMCPU_ASSERT_EMT_OR_NOT_RUNNING(pVCpu);
2222
2223 /* Construct. */
2224 PAPICCPU pApicCpu = VMCPU_TO_APICCPU(pVCpu);
2225 PAPIC pApic = VM_TO_APIC(pVCpu->CTX_SUFF(pVM));
2226 uint64_t uApicBaseMsr = MSR_IA32_APICBASE_ADDR;
2227 if (pVCpu->idCpu == 0)
2228 uApicBaseMsr |= MSR_IA32_APICBASE_BSP;
2229
2230 /* If the VM was configured with no APIC, don't enable xAPIC mode, obviously. */
2231 if (pApic->enmMaxMode != PDMAPICMODE_NONE)
2232 {
2233 uApicBaseMsr |= MSR_IA32_APICBASE_EN;
2234
2235 /*
2236 * While coming out of a reset the APIC is enabled and in xAPIC mode. If software had previously
2237 * disabled the APIC (which results in the CPUID bit being cleared as well) we re-enable it here.
2238 * See Intel spec. 10.12.5.1 "x2APIC States".
2239 */
2240 if (CPUMSetGuestCpuIdPerCpuApicFeature(pVCpu, true /*fVisible*/) == false)
2241 LogRel(("APIC%u: Resetting mode to xAPIC\n", pVCpu->idCpu));
2242 }
2243
2244 /* Commit. */
2245 ASMAtomicWriteU64(&pApicCpu->uApicBaseMsr, uApicBaseMsr);
2246}
2247
2248
2249/**
2250 * Initializes per-VCPU APIC to the state following an INIT reset
2251 * ("Wait-for-SIPI" state).
2252 *
2253 * @param pVCpu The cross context virtual CPU structure.
2254 */
2255void apicInitIpi(PVMCPUCC pVCpu)
2256{
2257 VMCPU_ASSERT_EMT_OR_NOT_RUNNING(pVCpu);
2258 PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
2259
2260 /*
2261 * See Intel spec. 10.4.7.3 "Local APIC State After an INIT Reset (Wait-for-SIPI State)"
2262 * and AMD spec 16.3.2 "APIC Registers".
2263 *
2264 * The reason we don't simply zero out the entire APIC page and only set the non-zero members
2265 * is because there are some registers that are not touched by the INIT IPI (e.g. version)
2266 * operation and this function is only a subset of the reset operation.
2267 */
2268 RT_ZERO(pXApicPage->irr);
2269 RT_ZERO(pXApicPage->irr);
2270 RT_ZERO(pXApicPage->isr);
2271 RT_ZERO(pXApicPage->tmr);
2272 RT_ZERO(pXApicPage->icr_hi);
2273 RT_ZERO(pXApicPage->icr_lo);
2274 RT_ZERO(pXApicPage->ldr);
2275 RT_ZERO(pXApicPage->tpr);
2276 RT_ZERO(pXApicPage->ppr);
2277 RT_ZERO(pXApicPage->timer_icr);
2278 RT_ZERO(pXApicPage->timer_ccr);
2279 RT_ZERO(pXApicPage->timer_dcr);
2280
2281 pXApicPage->dfr.u.u4Model = XAPICDESTFORMAT_FLAT;
2282 pXApicPage->dfr.u.u28ReservedMb1 = UINT32_C(0xfffffff);
2283
2284 /** @todo CMCI. */
2285
2286 RT_ZERO(pXApicPage->lvt_timer);
2287 pXApicPage->lvt_timer.u.u1Mask = 1;
2288
2289#if XAPIC_HARDWARE_VERSION == XAPIC_HARDWARE_VERSION_P4
2290 RT_ZERO(pXApicPage->lvt_thermal);
2291 pXApicPage->lvt_thermal.u.u1Mask = 1;
2292#endif
2293
2294 RT_ZERO(pXApicPage->lvt_perf);
2295 pXApicPage->lvt_perf.u.u1Mask = 1;
2296
2297 RT_ZERO(pXApicPage->lvt_lint0);
2298 pXApicPage->lvt_lint0.u.u1Mask = 1;
2299
2300 RT_ZERO(pXApicPage->lvt_lint1);
2301 pXApicPage->lvt_lint1.u.u1Mask = 1;
2302
2303 RT_ZERO(pXApicPage->lvt_error);
2304 pXApicPage->lvt_error.u.u1Mask = 1;
2305
2306 RT_ZERO(pXApicPage->svr);
2307 pXApicPage->svr.u.u8SpuriousVector = 0xff;
2308
2309 /* The self-IPI register is reset to 0. See Intel spec. 10.12.5.1 "x2APIC States" */
2310 PX2APICPAGE pX2ApicPage = VMCPU_TO_X2APICPAGE(pVCpu);
2311 RT_ZERO(pX2ApicPage->self_ipi);
2312
2313 /* Clear the pending-interrupt bitmaps. */
2314 PAPICCPU pApicCpu = VMCPU_TO_APICCPU(pVCpu);
2315 RT_BZERO(&pApicCpu->ApicPibLevel, sizeof(APICPIB));
2316 RT_BZERO(pApicCpu->CTX_SUFF(pvApicPib), sizeof(APICPIB));
2317
2318 /* Clear the interrupt line states for LINT0 and LINT1 pins. */
2319 pApicCpu->fActiveLint0 = false;
2320 pApicCpu->fActiveLint1 = false;
2321}
2322
2323
2324/**
2325 * Initializes per-VCPU APIC to the state following a power-up or hardware
2326 * reset.
2327 *
2328 * @param pVCpu The cross context virtual CPU structure.
2329 * @param fResetApicBaseMsr Whether to reset the APIC base MSR.
2330 */
2331void apicResetCpu(PVMCPUCC pVCpu, bool fResetApicBaseMsr)
2332{
2333 VMCPU_ASSERT_EMT_OR_NOT_RUNNING(pVCpu);
2334
2335 LogFlow(("APIC%u: apicR3ResetCpu: fResetApicBaseMsr=%RTbool\n", pVCpu->idCpu, fResetApicBaseMsr));
2336
2337#ifdef VBOX_STRICT
2338 /* Verify that the initial APIC ID reported via CPUID matches our VMCPU ID assumption. */
2339 uint32_t uEax, uEbx, uEcx, uEdx;
2340 uEax = uEbx = uEcx = uEdx = UINT32_MAX;
2341 CPUMGetGuestCpuId(pVCpu, 1, 0, -1 /*f64BitMode*/, &uEax, &uEbx, &uEcx, &uEdx);
2342 Assert(((uEbx >> 24) & 0xff) == pVCpu->idCpu);
2343#endif
2344
2345 /*
2346 * The state following a power-up or reset is a superset of the INIT state.
2347 * See Intel spec. 10.4.7.3 "Local APIC State After an INIT Reset ('Wait-for-SIPI' State)"
2348 */
2349 apicInitIpi(pVCpu);
2350
2351 /*
2352 * The APIC version register is read-only, so just initialize it here.
2353 * It is not clear from the specs, where exactly it is initialized.
2354 * The version determines the number of LVT entries and size of the APIC ID (8 bits for P4).
2355 */
2356 PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
2357#if XAPIC_HARDWARE_VERSION == XAPIC_HARDWARE_VERSION_P4
2358 pXApicPage->version.u.u8MaxLvtEntry = XAPIC_MAX_LVT_ENTRIES_P4 - 1;
2359 pXApicPage->version.u.u8Version = XAPIC_HARDWARE_VERSION_P4;
2360 AssertCompile(sizeof(pXApicPage->id.u8ApicId) >= XAPIC_APIC_ID_BIT_COUNT_P4 / 8);
2361#else
2362# error "Implement Pentium and P6 family APIC architectures"
2363#endif
2364
2365 /** @todo It isn't clear in the spec. where exactly the default base address
2366 * is (re)initialized, atm we do it here in Reset. */
2367 if (fResetApicBaseMsr)
2368 apicResetBaseMsr(pVCpu);
2369
2370 /*
2371 * Initialize the APIC ID register to xAPIC format.
2372 */
2373 ASMMemZero32(&pXApicPage->id, sizeof(pXApicPage->id));
2374 pXApicPage->id.u8ApicId = pVCpu->idCpu;
2375}
2376
2377
2378/**
2379 * Sets the APIC base MSR.
2380 *
2381 * @returns VBox status code - no informational ones, esp. not
2382 * VINF_CPUM_R3_MSR_WRITE. Only the following two:
2383 * @retval VINF_SUCCESS
2384 * @retval VERR_CPUM_RAISE_GP_0
2385 *
2386 * @param pVCpu The cross context virtual CPU structure.
2387 * @param u64BaseMsr The value to set.
2388 */
2389VMM_INT_DECL(int) APICSetBaseMsr(PVMCPUCC pVCpu, uint64_t u64BaseMsr)
2390{
2391 Assert(pVCpu);
2392
2393 PAPICCPU pApicCpu = VMCPU_TO_APICCPU(pVCpu);
2394 PAPIC pApic = VM_TO_APIC(pVCpu->CTX_SUFF(pVM));
2395 APICMODE enmOldMode = apicGetMode(pApicCpu->uApicBaseMsr);
2396 APICMODE enmNewMode = apicGetMode(u64BaseMsr);
2397 uint64_t uBaseMsr = pApicCpu->uApicBaseMsr;
2398
2399 Log2(("APIC%u: ApicSetBaseMsr: u64BaseMsr=%#RX64 enmNewMode=%s enmOldMode=%s\n", pVCpu->idCpu, u64BaseMsr,
2400 apicGetModeName(enmNewMode), apicGetModeName(enmOldMode)));
2401
2402 /*
2403 * We do not support re-mapping the APIC base address because:
2404 * - We'll have to manage all the mappings ourselves in the APIC (reference counting based unmapping etc.)
2405 * i.e. we can only unmap the MMIO region if no other APIC is mapped on that location.
2406 * - It's unclear how/if IOM can fallback to handling regions as regular memory (if the MMIO
2407 * region remains mapped but doesn't belong to the called VCPU's APIC).
2408 */
2409 /** @todo Handle per-VCPU APIC base relocation. */
2410 if (MSR_IA32_APICBASE_GET_ADDR(uBaseMsr) != MSR_IA32_APICBASE_ADDR)
2411 {
2412 if (pVCpu->apic.s.cLogMaxSetApicBaseAddr++ < 5)
2413 LogRel(("APIC%u: Attempt to relocate base to %#RGp, unsupported -> #GP(0)\n", pVCpu->idCpu,
2414 MSR_IA32_APICBASE_GET_ADDR(uBaseMsr)));
2415 return VERR_CPUM_RAISE_GP_0;
2416 }
2417
2418 /* Don't allow enabling xAPIC/x2APIC if the VM is configured with the APIC disabled. */
2419 if (pApic->enmMaxMode == PDMAPICMODE_NONE)
2420 {
2421 LogRel(("APIC%u: Disallowing APIC base MSR write as the VM is configured with APIC disabled!\n", pVCpu->idCpu));
2422 return apicMsrAccessError(pVCpu, MSR_IA32_APICBASE, APICMSRACCESS_WRITE_DISALLOWED_CONFIG);
2423 }
2424
2425 /*
2426 * Act on state transition.
2427 */
2428 if (enmNewMode != enmOldMode)
2429 {
2430 switch (enmNewMode)
2431 {
2432 case APICMODE_DISABLED:
2433 {
2434 /*
2435 * The APIC state needs to be reset (especially the APIC ID as x2APIC APIC ID bit layout
2436 * is different). We can start with a clean slate identical to the state after a power-up/reset.
2437 *
2438 * See Intel spec. 10.4.3 "Enabling or Disabling the Local APIC".
2439 *
2440 * We'll also manually manage the APIC base MSR here. We want a single-point of commit
2441 * at the end of this function rather than updating it in apicR3ResetCpu. This means we also
2442 * need to update the CPUID leaf ourselves.
2443 */
2444 apicResetCpu(pVCpu, false /* fResetApicBaseMsr */);
2445 uBaseMsr &= ~(MSR_IA32_APICBASE_EN | MSR_IA32_APICBASE_EXTD);
2446 CPUMSetGuestCpuIdPerCpuApicFeature(pVCpu, false /*fVisible*/);
2447 LogRel(("APIC%u: Switched mode to disabled\n", pVCpu->idCpu));
2448 break;
2449 }
2450
2451 case APICMODE_XAPIC:
2452 {
2453 if (enmOldMode != APICMODE_DISABLED)
2454 {
2455 LogRel(("APIC%u: Can only transition to xAPIC state from disabled state\n", pVCpu->idCpu));
2456 return apicMsrAccessError(pVCpu, MSR_IA32_APICBASE, APICMSRACCESS_WRITE_INVALID);
2457 }
2458
2459 uBaseMsr |= MSR_IA32_APICBASE_EN;
2460 CPUMSetGuestCpuIdPerCpuApicFeature(pVCpu, true /*fVisible*/);
2461 LogRel(("APIC%u: Switched mode to xAPIC\n", pVCpu->idCpu));
2462 break;
2463 }
2464
2465 case APICMODE_X2APIC:
2466 {
2467 if (pApic->enmMaxMode != PDMAPICMODE_X2APIC)
2468 {
2469 LogRel(("APIC%u: Disallowing transition to x2APIC mode as the VM is configured with the x2APIC disabled!\n",
2470 pVCpu->idCpu));
2471 return apicMsrAccessError(pVCpu, MSR_IA32_APICBASE, APICMSRACCESS_WRITE_INVALID);
2472 }
2473
2474 if (enmOldMode != APICMODE_XAPIC)
2475 {
2476 LogRel(("APIC%u: Can only transition to x2APIC state from xAPIC state\n", pVCpu->idCpu));
2477 return apicMsrAccessError(pVCpu, MSR_IA32_APICBASE, APICMSRACCESS_WRITE_INVALID);
2478 }
2479
2480 uBaseMsr |= MSR_IA32_APICBASE_EN | MSR_IA32_APICBASE_EXTD;
2481
2482 /*
2483 * The APIC ID needs updating when entering x2APIC mode.
2484 * Software written APIC ID in xAPIC mode isn't preserved.
2485 * The APIC ID becomes read-only to software in x2APIC mode.
2486 *
2487 * See Intel spec. 10.12.5.1 "x2APIC States".
2488 */
2489 PX2APICPAGE pX2ApicPage = VMCPU_TO_X2APICPAGE(pVCpu);
2490 ASMMemZero32(&pX2ApicPage->id, sizeof(pX2ApicPage->id));
2491 pX2ApicPage->id.u32ApicId = pVCpu->idCpu;
2492
2493 /*
2494 * LDR initialization occurs when entering x2APIC mode.
2495 * See Intel spec. 10.12.10.2 "Deriving Logical x2APIC ID from the Local x2APIC ID".
2496 */
2497 pX2ApicPage->ldr.u32LogicalApicId = ((pX2ApicPage->id.u32ApicId & UINT32_C(0xffff0)) << 16)
2498 | (UINT32_C(1) << pX2ApicPage->id.u32ApicId & UINT32_C(0xf));
2499
2500 LogRel(("APIC%u: Switched mode to x2APIC\n", pVCpu->idCpu));
2501 break;
2502 }
2503
2504 case APICMODE_INVALID:
2505 default:
2506 {
2507 Log(("APIC%u: Invalid state transition attempted\n", pVCpu->idCpu));
2508 return apicMsrAccessError(pVCpu, MSR_IA32_APICBASE, APICMSRACCESS_WRITE_INVALID);
2509 }
2510 }
2511 }
2512
2513 ASMAtomicWriteU64(&pApicCpu->uApicBaseMsr, uBaseMsr);
2514 return VINF_SUCCESS;
2515}
2516
2517
2518/**
2519 * Gets the APIC base MSR (no checks are performed wrt APIC hardware or its
2520 * state).
2521 *
2522 * @returns The base MSR value.
2523 * @param pVCpu The cross context virtual CPU structure.
2524 */
2525VMM_INT_DECL(uint64_t) APICGetBaseMsrNoCheck(PCVMCPUCC pVCpu)
2526{
2527 VMCPU_ASSERT_EMT_OR_NOT_RUNNING(pVCpu);
2528 PCAPICCPU pApicCpu = VMCPU_TO_APICCPU(pVCpu);
2529 return pApicCpu->uApicBaseMsr;
2530}
2531
2532
2533/**
2534 * Gets the APIC base MSR.
2535 *
2536 * @returns Strict VBox status code.
2537 * @param pVCpu The cross context virtual CPU structure.
2538 * @param pu64Value Where to store the MSR value.
2539 */
2540VMM_INT_DECL(VBOXSTRICTRC) APICGetBaseMsr(PVMCPUCC pVCpu, uint64_t *pu64Value)
2541{
2542 VMCPU_ASSERT_EMT_OR_NOT_RUNNING(pVCpu);
2543
2544 PCAPIC pApic = VM_TO_APIC(pVCpu->CTX_SUFF(pVM));
2545 if (pApic->enmMaxMode != PDMAPICMODE_NONE)
2546 {
2547 *pu64Value = APICGetBaseMsrNoCheck(pVCpu);
2548 return VINF_SUCCESS;
2549 }
2550
2551 if (pVCpu->apic.s.cLogMaxGetApicBaseAddr++ < 5)
2552 LogRel(("APIC%u: Reading APIC base MSR (%#x) when there is no APIC -> #GP(0)\n", pVCpu->idCpu, MSR_IA32_APICBASE));
2553 return VERR_CPUM_RAISE_GP_0;
2554}
2555
2556
2557/**
2558 * Sets the TPR (Task Priority Register).
2559 *
2560 * @retval VINF_SUCCESS
2561 * @retval VERR_CPUM_RAISE_GP_0
2562 * @retval VERR_PDM_NO_APIC_INSTANCE
2563 *
2564 * @param pVCpu The cross context virtual CPU structure.
2565 * @param u8Tpr The TPR value to set.
2566 */
2567VMMDECL(int) APICSetTpr(PVMCPUCC pVCpu, uint8_t u8Tpr)
2568{
2569 if (APICIsEnabled(pVCpu))
2570 return apicSetTprEx(pVCpu, u8Tpr, false /* fForceX2ApicBehaviour */);
2571 return VERR_PDM_NO_APIC_INSTANCE;
2572}
2573
2574
2575/**
2576 * Gets the highest priority pending interrupt.
2577 *
2578 * @returns true if any interrupt is pending, false otherwise.
2579 * @param pVCpu The cross context virtual CPU structure.
2580 * @param pu8PendingIntr Where to store the interrupt vector if the
2581 * interrupt is pending (optional, can be NULL).
2582 */
2583static bool apicGetHighestPendingInterrupt(PCVMCPUCC pVCpu, uint8_t *pu8PendingIntr)
2584{
2585 PCXAPICPAGE pXApicPage = VMCPU_TO_CXAPICPAGE(pVCpu);
2586 int const irrv = apicGetHighestSetBitInReg(&pXApicPage->irr, -1);
2587 if (irrv >= 0)
2588 {
2589 Assert(irrv <= (int)UINT8_MAX);
2590 if (pu8PendingIntr)
2591 *pu8PendingIntr = (uint8_t)irrv;
2592 return true;
2593 }
2594 return false;
2595}
2596
2597
2598/**
2599 * Gets the APIC TPR (Task Priority Register).
2600 *
2601 * @returns VBox status code.
2602 * @param pVCpu The cross context virtual CPU structure.
2603 * @param pu8Tpr Where to store the TPR.
2604 * @param pfPending Where to store whether there is a pending interrupt
2605 * (optional, can be NULL).
2606 * @param pu8PendingIntr Where to store the highest-priority pending
2607 * interrupt (optional, can be NULL).
2608 */
2609VMMDECL(int) APICGetTpr(PCVMCPUCC pVCpu, uint8_t *pu8Tpr, bool *pfPending, uint8_t *pu8PendingIntr)
2610{
2611 VMCPU_ASSERT_EMT(pVCpu);
2612 if (APICIsEnabled(pVCpu))
2613 {
2614 PCXAPICPAGE pXApicPage = VMCPU_TO_CXAPICPAGE(pVCpu);
2615 if (pfPending)
2616 {
2617 /*
2618 * Just return whatever the highest pending interrupt is in the IRR.
2619 * The caller is responsible for figuring out if it's masked by the TPR etc.
2620 */
2621 *pfPending = apicGetHighestPendingInterrupt(pVCpu, pu8PendingIntr);
2622 }
2623
2624 *pu8Tpr = pXApicPage->tpr.u8Tpr;
2625 return VINF_SUCCESS;
2626 }
2627
2628 *pu8Tpr = 0;
2629 return VERR_PDM_NO_APIC_INSTANCE;
2630}
2631
2632
2633/**
2634 * Gets the APIC timer frequency.
2635 *
2636 * @returns Strict VBox status code.
2637 * @param pVM The cross context VM structure.
2638 * @param pu64Value Where to store the timer frequency.
2639 */
2640VMM_INT_DECL(int) APICGetTimerFreq(PVMCC pVM, uint64_t *pu64Value)
2641{
2642 /*
2643 * Validate.
2644 */
2645 Assert(pVM);
2646 AssertPtrReturn(pu64Value, VERR_INVALID_PARAMETER);
2647
2648 PVMCPUCC pVCpu = pVM->CTX_SUFF(apCpus)[0];
2649 if (APICIsEnabled(pVCpu))
2650 {
2651 PCAPICCPU pApicCpu = VMCPU_TO_APICCPU(pVCpu);
2652 *pu64Value = PDMDevHlpTimerGetFreq(VMCPU_TO_DEVINS(pVCpu), pApicCpu->hTimer);
2653 return VINF_SUCCESS;
2654 }
2655 return VERR_PDM_NO_APIC_INSTANCE;
2656}
2657
2658
2659/**
2660 * Delivers an interrupt message via the system bus.
2661 *
2662 * @returns VBox status code.
2663 * @param pVM The cross context VM structure.
2664 * @param uDest The destination mask.
2665 * @param uDestMode The destination mode.
2666 * @param uDeliveryMode The delivery mode.
2667 * @param uVector The interrupt vector.
2668 * @param uPolarity The interrupt line polarity.
2669 * @param uTriggerMode The trigger mode.
2670 * @param uSrcTag The interrupt source tag (debugging).
2671 */
2672VMM_INT_DECL(int) APICBusDeliver(PVMCC pVM, uint8_t uDest, uint8_t uDestMode, uint8_t uDeliveryMode, uint8_t uVector,
2673 uint8_t uPolarity, uint8_t uTriggerMode, uint32_t uSrcTag)
2674{
2675 NOREF(uPolarity);
2676
2677 /*
2678 * If the APIC isn't enabled, do nothing and pretend success.
2679 */
2680 if (APICIsEnabled(pVM->CTX_SUFF(apCpus)[0]))
2681 { /* likely */ }
2682 else
2683 return VINF_SUCCESS;
2684
2685 /*
2686 * The destination field (mask) in the IO APIC redirectable table entry is 8-bits.
2687 * Hence, the broadcast mask is 0xff.
2688 * See IO APIC spec. 3.2.4. "IOREDTBL[23:0] - I/O Redirectable Table Registers".
2689 */
2690 XAPICTRIGGERMODE enmTriggerMode = (XAPICTRIGGERMODE)uTriggerMode;
2691 XAPICDELIVERYMODE enmDeliveryMode = (XAPICDELIVERYMODE)uDeliveryMode;
2692 XAPICDESTMODE enmDestMode = (XAPICDESTMODE)uDestMode;
2693 uint32_t fDestMask = uDest;
2694 uint32_t fBroadcastMask = UINT32_C(0xff);
2695
2696 Log2(("APIC: apicBusDeliver: fDestMask=%#x enmDestMode=%s enmTriggerMode=%s enmDeliveryMode=%s uVector=%#x uSrcTag=%#x\n",
2697 fDestMask, apicGetDestModeName(enmDestMode), apicGetTriggerModeName(enmTriggerMode),
2698 apicGetDeliveryModeName(enmDeliveryMode), uVector, uSrcTag));
2699
2700 bool fIntrAccepted;
2701 VMCPUSET DestCpuSet;
2702 apicGetDestCpuSet(pVM, fDestMask, fBroadcastMask, enmDestMode, enmDeliveryMode, &DestCpuSet);
2703 VBOXSTRICTRC rcStrict = apicSendIntr(pVM, NULL /* pVCpu */, uVector, enmTriggerMode, enmDeliveryMode, &DestCpuSet,
2704 &fIntrAccepted, uSrcTag, VINF_SUCCESS /* rcRZ */);
2705 if (fIntrAccepted)
2706 return VBOXSTRICTRC_VAL(rcStrict);
2707 return VERR_APIC_INTR_DISCARDED;
2708}
2709
2710
2711/**
2712 * Assert/de-assert the local APIC's LINT0/LINT1 interrupt pins.
2713 *
2714 * @returns Strict VBox status code.
2715 * @param pVCpu The cross context virtual CPU structure.
2716 * @param u8Pin The interrupt pin (0 for LINT0 or 1 for LINT1).
2717 * @param u8Level The level (0 for low or 1 for high).
2718 * @param rcRZ The return code if the operation cannot be performed in
2719 * the current context.
2720 *
2721 * @note All callers totally ignores the status code!
2722 */
2723VMM_INT_DECL(VBOXSTRICTRC) APICLocalInterrupt(PVMCPUCC pVCpu, uint8_t u8Pin, uint8_t u8Level, int rcRZ)
2724{
2725 AssertReturn(u8Pin <= 1, VERR_INVALID_PARAMETER);
2726 AssertReturn(u8Level <= 1, VERR_INVALID_PARAMETER);
2727
2728 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
2729
2730 /* If the APIC is enabled, the interrupt is subject to LVT programming. */
2731 if (APICIsEnabled(pVCpu))
2732 {
2733 PCXAPICPAGE pXApicPage = VMCPU_TO_CXAPICPAGE(pVCpu);
2734
2735 /* Pick the LVT entry corresponding to the interrupt pin. */
2736 static const uint16_t s_au16LvtOffsets[] =
2737 {
2738 XAPIC_OFF_LVT_LINT0,
2739 XAPIC_OFF_LVT_LINT1
2740 };
2741 Assert(u8Pin < RT_ELEMENTS(s_au16LvtOffsets));
2742 uint16_t const offLvt = s_au16LvtOffsets[u8Pin];
2743 uint32_t const uLvt = apicReadRaw32(pXApicPage, offLvt);
2744
2745 /* If software hasn't masked the interrupt in the LVT entry, proceed interrupt processing. */
2746 if (!XAPIC_LVT_IS_MASKED(uLvt))
2747 {
2748 XAPICDELIVERYMODE const enmDeliveryMode = XAPIC_LVT_GET_DELIVERY_MODE(uLvt);
2749 XAPICTRIGGERMODE enmTriggerMode = XAPIC_LVT_GET_TRIGGER_MODE(uLvt);
2750
2751 switch (enmDeliveryMode)
2752 {
2753 case XAPICDELIVERYMODE_INIT:
2754 {
2755 /** @todo won't work in R0/RC because callers don't care about rcRZ. */
2756 AssertMsgFailed(("INIT through LINT0/LINT1 is not yet supported\n"));
2757 }
2758 RT_FALL_THRU();
2759 case XAPICDELIVERYMODE_FIXED:
2760 {
2761 PAPICCPU pApicCpu = VMCPU_TO_APICCPU(pVCpu);
2762 uint8_t const uVector = XAPIC_LVT_GET_VECTOR(uLvt);
2763 bool fActive = RT_BOOL(u8Level & 1);
2764 bool volatile *pfActiveLine = u8Pin == 0 ? &pApicCpu->fActiveLint0 : &pApicCpu->fActiveLint1;
2765 /** @todo Polarity is busted elsewhere, we need to fix that
2766 * first. See @bugref{8386#c7}. */
2767#if 0
2768 uint8_t const u8Polarity = XAPIC_LVT_GET_POLARITY(uLvt);
2769 fActive ^= u8Polarity; */
2770#endif
2771 if (!fActive)
2772 {
2773 ASMAtomicCmpXchgBool(pfActiveLine, false, true);
2774 break;
2775 }
2776
2777 /* Level-sensitive interrupts are not supported for LINT1. See Intel spec. 10.5.1 "Local Vector Table". */
2778 if (offLvt == XAPIC_OFF_LVT_LINT1)
2779 enmTriggerMode = XAPICTRIGGERMODE_EDGE;
2780 /** @todo figure out what "If the local APIC is not used in conjunction with an I/O APIC and fixed
2781 delivery mode is selected; the Pentium 4, Intel Xeon, and P6 family processors will always
2782 use level-sensitive triggering, regardless if edge-sensitive triggering is selected."
2783 means. */
2784
2785 bool fSendIntr;
2786 if (enmTriggerMode == XAPICTRIGGERMODE_EDGE)
2787 {
2788 /* Recognize and send the interrupt only on an edge transition. */
2789 fSendIntr = ASMAtomicCmpXchgBool(pfActiveLine, true, false);
2790 }
2791 else
2792 {
2793 /* For level-triggered interrupts, redundant interrupts are not a problem. */
2794 Assert(enmTriggerMode == XAPICTRIGGERMODE_LEVEL);
2795 ASMAtomicCmpXchgBool(pfActiveLine, true, false);
2796
2797 /* Only when the remote IRR isn't set, set it and send the interrupt. */
2798 if (!(pXApicPage->lvt_lint0.all.u32LvtLint0 & XAPIC_LVT_REMOTE_IRR))
2799 {
2800 Assert(offLvt == XAPIC_OFF_LVT_LINT0);
2801 ASMAtomicOrU32((volatile uint32_t *)&pXApicPage->lvt_lint0.all.u32LvtLint0, XAPIC_LVT_REMOTE_IRR);
2802 fSendIntr = true;
2803 }
2804 else
2805 fSendIntr = false;
2806 }
2807
2808 if (fSendIntr)
2809 {
2810 VMCPUSET DestCpuSet;
2811 VMCPUSET_EMPTY(&DestCpuSet);
2812 VMCPUSET_ADD(&DestCpuSet, pVCpu->idCpu);
2813 rcStrict = apicSendIntr(pVCpu->CTX_SUFF(pVM), pVCpu, uVector, enmTriggerMode, enmDeliveryMode,
2814 &DestCpuSet, NULL /* pfIntrAccepted */, 0 /* uSrcTag */, rcRZ);
2815 }
2816 break;
2817 }
2818
2819 case XAPICDELIVERYMODE_SMI:
2820 case XAPICDELIVERYMODE_NMI:
2821 {
2822 VMCPUSET DestCpuSet;
2823 VMCPUSET_EMPTY(&DestCpuSet);
2824 VMCPUSET_ADD(&DestCpuSet, pVCpu->idCpu);
2825 uint8_t const uVector = XAPIC_LVT_GET_VECTOR(uLvt);
2826 rcStrict = apicSendIntr(pVCpu->CTX_SUFF(pVM), pVCpu, uVector, enmTriggerMode, enmDeliveryMode, &DestCpuSet,
2827 NULL /* pfIntrAccepted */, 0 /* uSrcTag */, rcRZ);
2828 break;
2829 }
2830
2831 case XAPICDELIVERYMODE_EXTINT:
2832 {
2833 Log2(("APIC%u: apicLocalInterrupt: %s ExtINT through LINT%u\n", pVCpu->idCpu,
2834 u8Level ? "Raising" : "Lowering", u8Pin));
2835 if (u8Level)
2836 apicSetInterruptFF(pVCpu, PDMAPICIRQ_EXTINT);
2837 else
2838 apicClearInterruptFF(pVCpu, PDMAPICIRQ_EXTINT);
2839 break;
2840 }
2841
2842 /* Reserved/unknown delivery modes: */
2843 case XAPICDELIVERYMODE_LOWEST_PRIO:
2844 case XAPICDELIVERYMODE_STARTUP:
2845 default:
2846 {
2847 AssertMsgFailed(("APIC%u: LocalInterrupt: Invalid delivery mode %#x (%s) on LINT%d\n", pVCpu->idCpu,
2848 enmDeliveryMode, apicGetDeliveryModeName(enmDeliveryMode), u8Pin));
2849 rcStrict = VERR_INTERNAL_ERROR_3;
2850 break;
2851 }
2852 }
2853 }
2854 }
2855 else
2856 {
2857 /* The APIC is hardware disabled. The CPU behaves as though there is no on-chip APIC. */
2858 if (u8Pin == 0)
2859 {
2860 /* LINT0 behaves as an external interrupt pin. */
2861 Log2(("APIC%u: apicLocalInterrupt: APIC hardware-disabled, %s INTR\n", pVCpu->idCpu,
2862 u8Level ? "raising" : "lowering"));
2863 if (u8Level)
2864 apicSetInterruptFF(pVCpu, PDMAPICIRQ_EXTINT);
2865 else
2866 apicClearInterruptFF(pVCpu, PDMAPICIRQ_EXTINT);
2867 }
2868 else
2869 {
2870 /* LINT1 behaves as NMI. */
2871 Log2(("APIC%u: apicLocalInterrupt: APIC hardware-disabled, raising NMI\n", pVCpu->idCpu));
2872 apicSetInterruptFF(pVCpu, PDMAPICIRQ_NMI);
2873 }
2874 }
2875
2876 return rcStrict;
2877}
2878
2879
2880/**
2881 * Gets the next highest-priority interrupt from the APIC, marking it as an
2882 * "in-service" interrupt.
2883 *
2884 * @returns VBox status code.
2885 * @param pVCpu The cross context virtual CPU structure.
2886 * @param pu8Vector Where to store the vector.
2887 * @param puSrcTag Where to store the interrupt source tag (debugging).
2888 */
2889VMM_INT_DECL(int) APICGetInterrupt(PVMCPUCC pVCpu, uint8_t *pu8Vector, uint32_t *puSrcTag)
2890{
2891 VMCPU_ASSERT_EMT(pVCpu);
2892 Assert(pu8Vector);
2893
2894 LogFlow(("APIC%u: apicGetInterrupt:\n", pVCpu->idCpu));
2895
2896 PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
2897 bool const fApicHwEnabled = APICIsEnabled(pVCpu);
2898 if ( fApicHwEnabled
2899 && pXApicPage->svr.u.fApicSoftwareEnable)
2900 {
2901 int const irrv = apicGetHighestSetBitInReg(&pXApicPage->irr, -1);
2902 if (RT_LIKELY(irrv >= 0))
2903 {
2904 Assert(irrv <= (int)UINT8_MAX);
2905 uint8_t const uVector = irrv;
2906
2907 /*
2908 * This can happen if the APIC receives an interrupt when the CPU has interrupts
2909 * disabled but the TPR is raised by the guest before re-enabling interrupts.
2910 */
2911 uint8_t const uTpr = pXApicPage->tpr.u8Tpr;
2912 if ( uTpr > 0
2913 && XAPIC_TPR_GET_TP(uVector) <= XAPIC_TPR_GET_TP(uTpr))
2914 {
2915 Log2(("APIC%u: apicGetInterrupt: Interrupt masked. uVector=%#x uTpr=%#x SpuriousVector=%#x\n", pVCpu->idCpu,
2916 uVector, uTpr, pXApicPage->svr.u.u8SpuriousVector));
2917 *pu8Vector = uVector;
2918 *puSrcTag = 0;
2919 STAM_COUNTER_INC(&pVCpu->apic.s.StatMaskedByTpr);
2920 return VERR_APIC_INTR_MASKED_BY_TPR;
2921 }
2922
2923 /*
2924 * The PPR should be up-to-date at this point through apicSetEoi().
2925 * We're on EMT so no parallel updates possible.
2926 * Subject the pending vector to PPR prioritization.
2927 */
2928 uint8_t const uPpr = pXApicPage->ppr.u8Ppr;
2929 if ( !uPpr
2930 || XAPIC_PPR_GET_PP(uVector) > XAPIC_PPR_GET_PP(uPpr))
2931 {
2932 apicClearVectorInReg(&pXApicPage->irr, uVector);
2933 apicSetVectorInReg(&pXApicPage->isr, uVector);
2934 apicUpdatePpr(pVCpu);
2935 apicSignalNextPendingIntr(pVCpu);
2936
2937 /* Retrieve the interrupt source tag associated with this interrupt. */
2938 PAPICCPU pApicCpu = VMCPU_TO_APICCPU(pVCpu);
2939 AssertCompile(RT_ELEMENTS(pApicCpu->auSrcTags) > UINT8_MAX);
2940 *puSrcTag = pApicCpu->auSrcTags[uVector];
2941 pApicCpu->auSrcTags[uVector] = 0;
2942
2943 Log2(("APIC%u: apicGetInterrupt: Valid Interrupt. uVector=%#x uSrcTag=%#x\n", pVCpu->idCpu, uVector, *puSrcTag));
2944 *pu8Vector = uVector;
2945 return VINF_SUCCESS;
2946 }
2947
2948 STAM_COUNTER_INC(&pVCpu->apic.s.StatMaskedByPpr);
2949 Log2(("APIC%u: apicGetInterrupt: Interrupt's priority is not higher than the PPR. uVector=%#x PPR=%#x\n",
2950 pVCpu->idCpu, uVector, uPpr));
2951 }
2952 else
2953 Log2(("APIC%u: apicGetInterrupt: No pending bits in IRR\n", pVCpu->idCpu));
2954 }
2955 else
2956 Log2(("APIC%u: apicGetInterrupt: APIC %s disabled\n", pVCpu->idCpu, !fApicHwEnabled ? "hardware" : "software"));
2957
2958 *pu8Vector = 0;
2959 *puSrcTag = 0;
2960 return VERR_APIC_INTR_NOT_PENDING;
2961}
2962
2963
2964/**
2965 * @callback_method_impl{FNIOMMMIONEWREAD}
2966 */
2967DECLCALLBACK(VBOXSTRICTRC) apicReadMmio(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
2968{
2969 NOREF(pvUser);
2970 Assert(!(off & 0xf));
2971 Assert(cb == 4); RT_NOREF_PV(cb);
2972
2973 PVMCPUCC pVCpu = PDMDevHlpGetVMCPU(pDevIns);
2974 uint16_t offReg = off & 0xff0;
2975 uint32_t uValue = 0;
2976
2977 STAM_COUNTER_INC(&pVCpu->apic.s.CTX_SUFF_Z(StatMmioRead));
2978
2979 VBOXSTRICTRC rc = VBOXSTRICTRC_VAL(apicReadRegister(pDevIns, pVCpu, offReg, &uValue));
2980 *(uint32_t *)pv = uValue;
2981
2982 Log2(("APIC%u: apicReadMmio: offReg=%#RX16 uValue=%#RX32\n", pVCpu->idCpu, offReg, uValue));
2983 return rc;
2984}
2985
2986
2987/**
2988 * @callback_method_impl{FNIOMMMIONEWWRITE}
2989 */
2990DECLCALLBACK(VBOXSTRICTRC) apicWriteMmio(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
2991{
2992 NOREF(pvUser);
2993 Assert(!(off & 0xf));
2994 Assert(cb == 4); RT_NOREF_PV(cb);
2995
2996 PVMCPUCC pVCpu = PDMDevHlpGetVMCPU(pDevIns);
2997 uint16_t offReg = off & 0xff0;
2998 uint32_t uValue = *(uint32_t *)pv;
2999
3000 STAM_COUNTER_INC(&pVCpu->apic.s.CTX_SUFF_Z(StatMmioWrite));
3001
3002 Log2(("APIC%u: apicWriteMmio: offReg=%#RX16 uValue=%#RX32\n", pVCpu->idCpu, offReg, uValue));
3003
3004 return apicWriteRegister(pDevIns, pVCpu, offReg, uValue);
3005}
3006
3007
3008/**
3009 * Sets the interrupt pending force-flag and pokes the EMT if required.
3010 *
3011 * @param pVCpu The cross context virtual CPU structure.
3012 * @param enmType The IRQ type.
3013 */
3014static void apicSetInterruptFF(PVMCPUCC pVCpu, PDMAPICIRQ enmType)
3015{
3016#ifdef IN_RING3
3017 /* IRQ state should be loaded as-is by "LoadExec". Changes can be made from LoadDone. */
3018 Assert(pVCpu->pVMR3->enmVMState != VMSTATE_LOADING || PDMR3HasLoadedState(pVCpu->pVMR3));
3019#endif
3020
3021 switch (enmType)
3022 {
3023 case PDMAPICIRQ_HARDWARE:
3024 VMCPU_ASSERT_EMT_OR_NOT_RUNNING(pVCpu);
3025 VMCPU_FF_SET(pVCpu, VMCPU_FF_INTERRUPT_APIC);
3026 break;
3027 case PDMAPICIRQ_UPDATE_PENDING: VMCPU_FF_SET(pVCpu, VMCPU_FF_UPDATE_APIC); break;
3028 case PDMAPICIRQ_NMI: VMCPU_FF_SET(pVCpu, VMCPU_FF_INTERRUPT_NMI); break;
3029 case PDMAPICIRQ_SMI: VMCPU_FF_SET(pVCpu, VMCPU_FF_INTERRUPT_SMI); break;
3030 case PDMAPICIRQ_EXTINT: VMCPU_FF_SET(pVCpu, VMCPU_FF_INTERRUPT_PIC); break;
3031 default:
3032 AssertMsgFailed(("enmType=%d\n", enmType));
3033 break;
3034 }
3035
3036 /*
3037 * We need to wake up the target CPU if we're not on EMT.
3038 */
3039 /** @todo r=bird: Why do we skip this waking up for PDMAPICIRQ_HARDWARE? */
3040 /** @todo r=bird: We could just use RTThreadNativeSelf() here, couldn't we? */
3041#if defined(IN_RING0)
3042 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
3043 VMCPUID idCpu = pVCpu->idCpu;
3044 if ( enmType != PDMAPICIRQ_HARDWARE
3045 && VMMGetCpuId(pVM) != idCpu)
3046 {
3047 switch (VMCPU_GET_STATE(pVCpu))
3048 {
3049 case VMCPUSTATE_STARTED_EXEC:
3050 Log7Func(("idCpu=%u VMCPUSTATE_STARTED_EXEC\n", idCpu));
3051 GVMMR0SchedPokeNoGVMNoLock(pVM, idCpu);
3052 break;
3053
3054 case VMCPUSTATE_STARTED_HALTED:
3055 Log7Func(("idCpu=%u VMCPUSTATE_STARTED_HALTED\n", idCpu));
3056 GVMMR0SchedWakeUpNoGVMNoLock(pVM, idCpu);
3057 break;
3058
3059 default:
3060 Log7Func(("idCpu=%u enmState=%d\n", idCpu, pVCpu->enmState));
3061 break; /* nothing to do in other states. */
3062 }
3063 }
3064#elif defined(IN_RING3)
3065 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
3066 VMCPUID idCpu = pVCpu->idCpu;
3067 if ( enmType != PDMAPICIRQ_HARDWARE
3068 && VMMGetCpuId(pVM) != idCpu)
3069 {
3070 Log7Func(("idCpu=%u enmState=%d\n", idCpu, pVCpu->enmState));
3071 VMR3NotifyCpuFFU(pVCpu->pUVCpu, VMNOTIFYFF_FLAGS_DONE_REM | VMNOTIFYFF_FLAGS_POKE);
3072 }
3073#endif
3074}
3075
3076
3077/**
3078 * Clears the interrupt pending force-flag.
3079 *
3080 * @param pVCpu The cross context virtual CPU structure.
3081 * @param enmType The IRQ type.
3082 */
3083void apicClearInterruptFF(PVMCPUCC pVCpu, PDMAPICIRQ enmType)
3084{
3085#ifdef IN_RING3
3086 /* IRQ state should be loaded as-is by "LoadExec". Changes can be made from LoadDone. */
3087 Assert(pVCpu->pVMR3->enmVMState != VMSTATE_LOADING || PDMR3HasLoadedState(pVCpu->pVMR3));
3088#endif
3089
3090 /* NMI/SMI can't be cleared. */
3091 switch (enmType)
3092 {
3093 case PDMAPICIRQ_HARDWARE: VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_APIC); break;
3094 case PDMAPICIRQ_EXTINT: VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_PIC); break;
3095 default:
3096 AssertMsgFailed(("enmType=%d\n", enmType));
3097 break;
3098 }
3099}
3100
3101
3102/**
3103 * Posts an interrupt to a target APIC.
3104 *
3105 * This function handles interrupts received from the system bus or
3106 * interrupts generated locally from the LVT or via a self IPI.
3107 *
3108 * Don't use this function to try and deliver ExtINT style interrupts.
3109 *
3110 * @returns true if the interrupt was accepted, false otherwise.
3111 * @param pVCpu The cross context virtual CPU structure.
3112 * @param uVector The vector of the interrupt to be posted.
3113 * @param enmTriggerMode The trigger mode of the interrupt.
3114 * @param uSrcTag The interrupt source tag (debugging).
3115 *
3116 * @thread Any.
3117 */
3118bool apicPostInterrupt(PVMCPUCC pVCpu, uint8_t uVector, XAPICTRIGGERMODE enmTriggerMode, uint32_t uSrcTag)
3119{
3120 Assert(pVCpu);
3121 Assert(uVector > XAPIC_ILLEGAL_VECTOR_END);
3122
3123 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
3124 PCAPIC pApic = VM_TO_APIC(pVM);
3125 PAPICCPU pApicCpu = VMCPU_TO_APICCPU(pVCpu);
3126 bool fAccepted = true;
3127
3128 STAM_PROFILE_START(&pApicCpu->StatPostIntr, a);
3129 STAM_REL_COUNTER_INC(&pApicCpu->StatPostIntrCnt);
3130 STAM_REL_COUNTER_INC(&pApicCpu->aStatVectors[uVector]);
3131
3132 /*
3133 * Only post valid interrupt vectors.
3134 * See Intel spec. 10.5.2 "Valid Interrupt Vectors".
3135 */
3136 if (RT_LIKELY(uVector > XAPIC_ILLEGAL_VECTOR_END))
3137 {
3138 /*
3139 * If the interrupt is already pending in the IRR we can skip the
3140 * potential expensive operation of poking the guest EMT out of execution.
3141 */
3142 PCXAPICPAGE pXApicPage = VMCPU_TO_CXAPICPAGE(pVCpu);
3143 if (!apicTestVectorInReg(&pXApicPage->irr, uVector)) /* PAV */
3144 {
3145 /* Update the interrupt source tag (debugging). */
3146 if (!pApicCpu->auSrcTags[uVector])
3147 pApicCpu->auSrcTags[uVector] = uSrcTag;
3148 else
3149 pApicCpu->auSrcTags[uVector] |= RT_BIT_32(31);
3150
3151 Log2(("APIC: apicPostInterrupt: SrcCpu=%u TargetCpu=%u uVector=%#x %s\n",
3152 VMMGetCpuId(pVM), pVCpu->idCpu, uVector, enmTriggerMode == XAPICTRIGGERMODE_EDGE ? "edge" : "lvl"));
3153 if (enmTriggerMode == XAPICTRIGGERMODE_EDGE)
3154 {
3155 if (pApic->fPostedIntrsEnabled)
3156 { /** @todo posted-interrupt call to hardware */ }
3157 else
3158 {
3159 apicSetVectorInPib(pApicCpu->CTX_SUFF(pvApicPib), uVector);
3160 uint32_t const fAlreadySet = apicSetNotificationBitInPib((PAPICPIB)pApicCpu->CTX_SUFF(pvApicPib));
3161 if (!fAlreadySet)
3162 {
3163 Log2(("APIC: apicPostInterrupt: Setting UPDATE_APIC FF for edge-triggered intr. uVector=%#x\n", uVector));
3164 apicSetInterruptFF(pVCpu, PDMAPICIRQ_UPDATE_PENDING);
3165 }
3166 }
3167 }
3168 else
3169 {
3170 /*
3171 * Level-triggered interrupts requires updating of the TMR and thus cannot be
3172 * delivered asynchronously.
3173 */
3174 apicSetVectorInPib(&pApicCpu->ApicPibLevel, uVector);
3175 uint32_t const fAlreadySet = apicSetNotificationBitInPib(&pApicCpu->ApicPibLevel);
3176 if (!fAlreadySet)
3177 {
3178 Log2(("APIC: apicPostInterrupt: Setting UPDATE_APIC FF for level-triggered intr. uVector=%#x\n", uVector));
3179 apicSetInterruptFF(pVCpu, PDMAPICIRQ_UPDATE_PENDING);
3180 }
3181 }
3182 }
3183 else
3184 {
3185 Log2(("APIC: apicPostInterrupt: SrcCpu=%u TargetCpu=%u. Vector %#x Already in IRR, skipping\n", VMMGetCpuId(pVM),
3186 pVCpu->idCpu, uVector));
3187 STAM_COUNTER_INC(&pApicCpu->StatPostIntrAlreadyPending);
3188 }
3189 }
3190 else
3191 {
3192 fAccepted = false;
3193 apicSetError(pVCpu, XAPIC_ESR_RECV_ILLEGAL_VECTOR);
3194 }
3195
3196 STAM_PROFILE_STOP(&pApicCpu->StatPostIntr, a);
3197 return fAccepted;
3198}
3199
3200
3201/**
3202 * Starts the APIC timer.
3203 *
3204 * @param pVCpu The cross context virtual CPU structure.
3205 * @param uInitialCount The timer's Initial-Count Register (ICR), must be >
3206 * 0.
3207 * @thread Any.
3208 */
3209void apicStartTimer(PVMCPUCC pVCpu, uint32_t uInitialCount)
3210{
3211 Assert(pVCpu);
3212 PAPICCPU pApicCpu = VMCPU_TO_APICCPU(pVCpu);
3213 PPDMDEVINS pDevIns = VMCPU_TO_DEVINS(pVCpu);
3214 Assert(PDMDevHlpTimerIsLockOwner(pDevIns, pApicCpu->hTimer));
3215 Assert(uInitialCount > 0);
3216
3217 PCXAPICPAGE pXApicPage = APICCPU_TO_CXAPICPAGE(pApicCpu);
3218 uint8_t const uTimerShift = apicGetTimerShift(pXApicPage);
3219 uint64_t const cTicksToNext = (uint64_t)uInitialCount << uTimerShift;
3220
3221 Log2(("APIC%u: apicStartTimer: uInitialCount=%#RX32 uTimerShift=%u cTicksToNext=%RU64\n", pVCpu->idCpu, uInitialCount,
3222 uTimerShift, cTicksToNext));
3223
3224 /*
3225 * The assumption here is that the timer doesn't tick during this call
3226 * and thus setting a relative time to fire next is accurate. The advantage
3227 * however is updating u64TimerInitial 'atomically' while setting the next
3228 * tick.
3229 */
3230 PDMDevHlpTimerSetRelative(pDevIns, pApicCpu->hTimer, cTicksToNext, &pApicCpu->u64TimerInitial);
3231 apicHintTimerFreq(pDevIns, pApicCpu, uInitialCount, uTimerShift);
3232}
3233
3234
3235/**
3236 * Stops the APIC timer.
3237 *
3238 * @param pVCpu The cross context virtual CPU structure.
3239 * @thread Any.
3240 */
3241static void apicStopTimer(PVMCPUCC pVCpu)
3242{
3243 Assert(pVCpu);
3244 PAPICCPU pApicCpu = VMCPU_TO_APICCPU(pVCpu);
3245 PPDMDEVINS pDevIns = VMCPU_TO_DEVINS(pVCpu);
3246 Assert(PDMDevHlpTimerIsLockOwner(pDevIns, pApicCpu->hTimer));
3247
3248 Log2(("APIC%u: apicStopTimer\n", pVCpu->idCpu));
3249
3250 PDMDevHlpTimerStop(pDevIns, pApicCpu->hTimer); /* This will reset the hint, no need to explicitly call TMTimerSetFrequencyHint(). */
3251 pApicCpu->uHintedTimerInitialCount = 0;
3252 pApicCpu->uHintedTimerShift = 0;
3253}
3254
3255
3256/**
3257 * Queues a pending interrupt as in-service.
3258 *
3259 * This function should only be needed without virtualized APIC
3260 * registers. With virtualized APIC registers, it's sufficient to keep
3261 * the interrupts pending in the IRR as the hardware takes care of
3262 * virtual interrupt delivery.
3263 *
3264 * @returns true if the interrupt was queued to in-service interrupts,
3265 * false otherwise.
3266 * @param pVCpu The cross context virtual CPU structure.
3267 * @param u8PendingIntr The pending interrupt to queue as
3268 * in-service.
3269 *
3270 * @remarks This assumes the caller has done the necessary checks and
3271 * is ready to take actually service the interrupt (TPR,
3272 * interrupt shadow etc.)
3273 */
3274VMM_INT_DECL(bool) APICQueueInterruptToService(PVMCPUCC pVCpu, uint8_t u8PendingIntr)
3275{
3276 VMCPU_ASSERT_EMT(pVCpu);
3277
3278 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
3279 PAPIC pApic = VM_TO_APIC(pVM);
3280 Assert(!pApic->fVirtApicRegsEnabled);
3281 NOREF(pApic);
3282
3283 PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
3284 bool const fIsPending = apicTestVectorInReg(&pXApicPage->irr, u8PendingIntr);
3285 if (fIsPending)
3286 {
3287 apicClearVectorInReg(&pXApicPage->irr, u8PendingIntr);
3288 apicSetVectorInReg(&pXApicPage->isr, u8PendingIntr);
3289 apicUpdatePpr(pVCpu);
3290 return true;
3291 }
3292 return false;
3293}
3294
3295
3296/**
3297 * De-queues a pending interrupt from in-service.
3298 *
3299 * This undoes APICQueueInterruptToService() for premature VM-exits before event
3300 * injection.
3301 *
3302 * @param pVCpu The cross context virtual CPU structure.
3303 * @param u8PendingIntr The pending interrupt to de-queue from
3304 * in-service.
3305 */
3306VMM_INT_DECL(void) APICDequeueInterruptFromService(PVMCPUCC pVCpu, uint8_t u8PendingIntr)
3307{
3308 VMCPU_ASSERT_EMT(pVCpu);
3309
3310 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
3311 PAPIC pApic = VM_TO_APIC(pVM);
3312 Assert(!pApic->fVirtApicRegsEnabled);
3313 NOREF(pApic);
3314
3315 PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
3316 bool const fInService = apicTestVectorInReg(&pXApicPage->isr, u8PendingIntr);
3317 if (fInService)
3318 {
3319 apicClearVectorInReg(&pXApicPage->isr, u8PendingIntr);
3320 apicSetVectorInReg(&pXApicPage->irr, u8PendingIntr);
3321 apicUpdatePpr(pVCpu);
3322 }
3323}
3324
3325
3326/**
3327 * Updates pending interrupts from the pending-interrupt bitmaps to the IRR.
3328 *
3329 * @param pVCpu The cross context virtual CPU structure.
3330 *
3331 * @note NEM/win is ASSUMING the an up to date TPR is not required here.
3332 */
3333VMMDECL(void) APICUpdatePendingInterrupts(PVMCPUCC pVCpu)
3334{
3335 VMCPU_ASSERT_EMT_OR_NOT_RUNNING(pVCpu);
3336
3337 PAPICCPU pApicCpu = VMCPU_TO_APICCPU(pVCpu);
3338 PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
3339 bool fHasPendingIntrs = false;
3340
3341 Log3(("APIC%u: APICUpdatePendingInterrupts:\n", pVCpu->idCpu));
3342 STAM_PROFILE_START(&pApicCpu->StatUpdatePendingIntrs, a);
3343
3344 /* Update edge-triggered pending interrupts. */
3345 PAPICPIB pPib = (PAPICPIB)pApicCpu->CTX_SUFF(pvApicPib);
3346 for (;;)
3347 {
3348 uint32_t const fAlreadySet = apicClearNotificationBitInPib((PAPICPIB)pApicCpu->CTX_SUFF(pvApicPib));
3349 if (!fAlreadySet)
3350 break;
3351
3352 AssertCompile(RT_ELEMENTS(pXApicPage->irr.u) == 2 * RT_ELEMENTS(pPib->au64VectorBitmap));
3353 for (size_t idxPib = 0, idxReg = 0; idxPib < RT_ELEMENTS(pPib->au64VectorBitmap); idxPib++, idxReg += 2)
3354 {
3355 uint64_t const u64Fragment = ASMAtomicXchgU64(&pPib->au64VectorBitmap[idxPib], 0);
3356 if (u64Fragment)
3357 {
3358 uint32_t const u32FragmentLo = RT_LO_U32(u64Fragment);
3359 uint32_t const u32FragmentHi = RT_HI_U32(u64Fragment);
3360 Log6Func(("edge[%u/%u]: %'016RX64: irr=%08RX32'%08RX32 |; tmr=%08RX32'%08RX32 &~\n", idxPib, idxReg, u64Fragment,
3361 pXApicPage->irr.u[idxReg].u32Reg, pXApicPage->irr.u[idxReg + 1].u32Reg,
3362 pXApicPage->tmr.u[idxReg].u32Reg, pXApicPage->tmr.u[idxReg + 1].u32Reg));
3363
3364 pXApicPage->irr.u[idxReg].u32Reg |= u32FragmentLo;
3365 pXApicPage->irr.u[idxReg + 1].u32Reg |= u32FragmentHi;
3366
3367 pXApicPage->tmr.u[idxReg].u32Reg &= ~u32FragmentLo;
3368 pXApicPage->tmr.u[idxReg + 1].u32Reg &= ~u32FragmentHi;
3369 fHasPendingIntrs = true;
3370 }
3371 }
3372 }
3373
3374 /* Update level-triggered pending interrupts. */
3375 pPib = (PAPICPIB)&pApicCpu->ApicPibLevel;
3376 for (;;)
3377 {
3378 uint32_t const fAlreadySet = apicClearNotificationBitInPib((PAPICPIB)&pApicCpu->ApicPibLevel);
3379 if (!fAlreadySet)
3380 break;
3381
3382 AssertCompile(RT_ELEMENTS(pXApicPage->irr.u) == 2 * RT_ELEMENTS(pPib->au64VectorBitmap));
3383 for (size_t idxPib = 0, idxReg = 0; idxPib < RT_ELEMENTS(pPib->au64VectorBitmap); idxPib++, idxReg += 2)
3384 {
3385 uint64_t const u64Fragment = ASMAtomicXchgU64(&pPib->au64VectorBitmap[idxPib], 0);
3386 if (u64Fragment)
3387 {
3388 Log6Func(("level[%u/%u]: %'016RX64: irr=%08RX32'%08RX32 |; tmr=%08RX32'%08RX32 |\n", idxPib, idxReg, u64Fragment,
3389 pXApicPage->irr.u[idxReg].u32Reg, pXApicPage->irr.u[idxReg + 1].u32Reg,
3390 pXApicPage->tmr.u[idxReg].u32Reg, pXApicPage->tmr.u[idxReg + 1].u32Reg));
3391 uint32_t const u32FragmentLo = RT_LO_U32(u64Fragment);
3392 uint32_t const u32FragmentHi = RT_HI_U32(u64Fragment);
3393
3394 pXApicPage->irr.u[idxReg].u32Reg |= u32FragmentLo;
3395 pXApicPage->irr.u[idxReg + 1].u32Reg |= u32FragmentHi;
3396
3397 pXApicPage->tmr.u[idxReg].u32Reg |= u32FragmentLo;
3398 pXApicPage->tmr.u[idxReg + 1].u32Reg |= u32FragmentHi;
3399 fHasPendingIntrs = true;
3400 }
3401 }
3402 }
3403
3404 STAM_PROFILE_STOP(&pApicCpu->StatUpdatePendingIntrs, a);
3405 Log3(("APIC%u: APICUpdatePendingInterrupts: fHasPendingIntrs=%RTbool\n", pVCpu->idCpu, fHasPendingIntrs));
3406
3407 if ( fHasPendingIntrs
3408 && !VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_APIC))
3409 apicSignalNextPendingIntr(pVCpu);
3410}
3411
3412
3413/**
3414 * Gets the highest priority pending interrupt.
3415 *
3416 * @returns true if any interrupt is pending, false otherwise.
3417 * @param pVCpu The cross context virtual CPU structure.
3418 * @param pu8PendingIntr Where to store the interrupt vector if the
3419 * interrupt is pending.
3420 */
3421VMM_INT_DECL(bool) APICGetHighestPendingInterrupt(PVMCPUCC pVCpu, uint8_t *pu8PendingIntr)
3422{
3423 VMCPU_ASSERT_EMT(pVCpu);
3424 return apicGetHighestPendingInterrupt(pVCpu, pu8PendingIntr);
3425}
3426
3427
3428/**
3429 * Posts an interrupt to a target APIC, Hyper-V interface.
3430 *
3431 * @param pVCpu The cross context virtual CPU structure.
3432 * @param uVector The vector of the interrupt to be posted.
3433 * @param fAutoEoi Whether this interrupt has automatic EOI
3434 * treatment.
3435 * @param enmTriggerMode The trigger mode of the interrupt.
3436 *
3437 * @thread Any.
3438 */
3439VMM_INT_DECL(void) APICHvSendInterrupt(PVMCPUCC pVCpu, uint8_t uVector, bool fAutoEoi, XAPICTRIGGERMODE enmTriggerMode)
3440{
3441 Assert(pVCpu);
3442 Assert(!fAutoEoi); /** @todo AutoEOI. */
3443 RT_NOREF(fAutoEoi);
3444 apicPostInterrupt(pVCpu, uVector, enmTriggerMode, 0 /* uSrcTag */);
3445}
3446
3447
3448/**
3449 * Sets the Task Priority Register (TPR), Hyper-V interface.
3450 *
3451 * @returns Strict VBox status code.
3452 * @param pVCpu The cross context virtual CPU structure.
3453 * @param uTpr The TPR value to set.
3454 *
3455 * @remarks Validates like in x2APIC mode.
3456 */
3457VMM_INT_DECL(VBOXSTRICTRC) APICHvSetTpr(PVMCPUCC pVCpu, uint8_t uTpr)
3458{
3459 Assert(pVCpu);
3460 VMCPU_ASSERT_EMT(pVCpu);
3461 return apicSetTprEx(pVCpu, uTpr, true /* fForceX2ApicBehaviour */);
3462}
3463
3464
3465/**
3466 * Gets the Task Priority Register (TPR), Hyper-V interface.
3467 *
3468 * @returns The TPR value.
3469 * @param pVCpu The cross context virtual CPU structure.
3470 */
3471VMM_INT_DECL(uint8_t) APICHvGetTpr(PVMCPUCC pVCpu)
3472{
3473 Assert(pVCpu);
3474 VMCPU_ASSERT_EMT(pVCpu);
3475
3476 /*
3477 * The APIC could be operating in xAPIC mode and thus we should not use the apicReadMsr()
3478 * interface which validates the APIC mode and will throw a #GP(0) if not in x2APIC mode.
3479 * We could use the apicReadRegister() MMIO interface, but why bother getting the PDMDEVINS
3480 * pointer, so just directly read the APIC page.
3481 */
3482 PCXAPICPAGE pXApicPage = VMCPU_TO_CXAPICPAGE(pVCpu);
3483 return apicReadRaw32(pXApicPage, XAPIC_OFF_TPR);
3484}
3485
3486
3487/**
3488 * Sets the Interrupt Command Register (ICR), Hyper-V interface.
3489 *
3490 * @returns Strict VBox status code.
3491 * @param pVCpu The cross context virtual CPU structure.
3492 * @param uIcr The ICR value to set.
3493 */
3494VMM_INT_DECL(VBOXSTRICTRC) APICHvSetIcr(PVMCPUCC pVCpu, uint64_t uIcr)
3495{
3496 Assert(pVCpu);
3497 VMCPU_ASSERT_EMT(pVCpu);
3498 return apicSetIcr(pVCpu, uIcr, VINF_CPUM_R3_MSR_WRITE);
3499}
3500
3501
3502/**
3503 * Gets the Interrupt Command Register (ICR), Hyper-V interface.
3504 *
3505 * @returns The ICR value.
3506 * @param pVCpu The cross context virtual CPU structure.
3507 */
3508VMM_INT_DECL(uint64_t) APICHvGetIcr(PVMCPUCC pVCpu)
3509{
3510 Assert(pVCpu);
3511 VMCPU_ASSERT_EMT(pVCpu);
3512 return apicGetIcrNoCheck(pVCpu);
3513}
3514
3515
3516/**
3517 * Sets the End-Of-Interrupt (EOI) register, Hyper-V interface.
3518 *
3519 * @returns Strict VBox status code.
3520 * @param pVCpu The cross context virtual CPU structure.
3521 * @param uEoi The EOI value.
3522 */
3523VMM_INT_DECL(VBOXSTRICTRC) APICHvSetEoi(PVMCPUCC pVCpu, uint32_t uEoi)
3524{
3525 Assert(pVCpu);
3526 VMCPU_ASSERT_EMT_OR_NOT_RUNNING(pVCpu);
3527 return apicSetEoi(pVCpu, uEoi, true /* fForceX2ApicBehaviour */);
3528}
3529
3530
3531/**
3532 * Gets the APIC page pointers for the specified VCPU.
3533 *
3534 * @returns VBox status code.
3535 * @param pVCpu The cross context virtual CPU structure.
3536 * @param pHCPhys Where to store the host-context physical address.
3537 * @param pR0Ptr Where to store the ring-0 address.
3538 * @param pR3Ptr Where to store the ring-3 address (optional).
3539 */
3540VMM_INT_DECL(int) APICGetApicPageForCpu(PCVMCPUCC pVCpu, PRTHCPHYS pHCPhys, PRTR0PTR pR0Ptr, PRTR3PTR pR3Ptr)
3541{
3542 AssertReturn(pVCpu, VERR_INVALID_PARAMETER);
3543 AssertReturn(pHCPhys, VERR_INVALID_PARAMETER);
3544 AssertReturn(pR0Ptr, VERR_INVALID_PARAMETER);
3545
3546 Assert(PDMHasApic(pVCpu->CTX_SUFF(pVM)));
3547
3548 PCAPICCPU pApicCpu = VMCPU_TO_APICCPU(pVCpu);
3549 *pHCPhys = pApicCpu->HCPhysApicPage;
3550 *pR0Ptr = pApicCpu->pvApicPageR0;
3551 if (pR3Ptr)
3552 *pR3Ptr = pApicCpu->pvApicPageR3;
3553 return VINF_SUCCESS;
3554}
3555
3556#ifndef IN_RING3
3557
3558/**
3559 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
3560 */
3561static DECLCALLBACK(int) apicRZConstruct(PPDMDEVINS pDevIns)
3562{
3563 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
3564 PAPICDEV pThis = PDMDEVINS_2_DATA(pDevIns, PAPICDEV);
3565 PVMCC pVM = PDMDevHlpGetVM(pDevIns);
3566
3567 pVM->apicr0.s.pDevInsR0 = pDevIns;
3568
3569 int rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
3570 AssertRCReturn(rc, rc);
3571
3572 rc = PDMDevHlpApicSetUpContext(pDevIns);
3573 AssertRCReturn(rc, rc);
3574
3575 rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hMmio, apicWriteMmio, apicReadMmio, NULL /*pvUser*/);
3576 AssertRCReturn(rc, rc);
3577
3578 return VINF_SUCCESS;
3579}
3580#endif /* !IN_RING3 */
3581
3582/**
3583 * APIC device registration structure.
3584 */
3585const PDMDEVREG g_DeviceAPIC =
3586{
3587 /* .u32Version = */ PDM_DEVREG_VERSION,
3588 /* .uReserved0 = */ 0,
3589 /* .szName = */ "apic",
3590 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE
3591 | PDM_DEVREG_FLAGS_REQUIRE_R0 | PDM_DEVREG_FLAGS_REQUIRE_RC,
3592 /* .fClass = */ PDM_DEVREG_CLASS_PIC,
3593 /* .cMaxInstances = */ 1,
3594 /* .uSharedVersion = */ 42,
3595 /* .cbInstanceShared = */ sizeof(APICDEV),
3596 /* .cbInstanceCC = */ 0,
3597 /* .cbInstanceRC = */ 0,
3598 /* .cMaxPciDevices = */ 0,
3599 /* .cMaxMsixVectors = */ 0,
3600 /* .pszDescription = */ "Advanced Programmable Interrupt Controller",
3601#if defined(IN_RING3)
3602 /* .szRCMod = */ "VMMRC.rc",
3603 /* .szR0Mod = */ "VMMR0.r0",
3604 /* .pfnConstruct = */ apicR3Construct,
3605 /* .pfnDestruct = */ apicR3Destruct,
3606 /* .pfnRelocate = */ apicR3Relocate,
3607 /* .pfnMemSetup = */ NULL,
3608 /* .pfnPowerOn = */ NULL,
3609 /* .pfnReset = */ apicR3Reset,
3610 /* .pfnSuspend = */ NULL,
3611 /* .pfnResume = */ NULL,
3612 /* .pfnAttach = */ NULL,
3613 /* .pfnDetach = */ NULL,
3614 /* .pfnQueryInterface = */ NULL,
3615 /* .pfnInitComplete = */ apicR3InitComplete,
3616 /* .pfnPowerOff = */ NULL,
3617 /* .pfnSoftReset = */ NULL,
3618 /* .pfnReserved0 = */ NULL,
3619 /* .pfnReserved1 = */ NULL,
3620 /* .pfnReserved2 = */ NULL,
3621 /* .pfnReserved3 = */ NULL,
3622 /* .pfnReserved4 = */ NULL,
3623 /* .pfnReserved5 = */ NULL,
3624 /* .pfnReserved6 = */ NULL,
3625 /* .pfnReserved7 = */ NULL,
3626#elif defined(IN_RING0)
3627 /* .pfnEarlyConstruct = */ NULL,
3628 /* .pfnConstruct = */ apicRZConstruct,
3629 /* .pfnDestruct = */ NULL,
3630 /* .pfnFinalDestruct = */ NULL,
3631 /* .pfnRequest = */ NULL,
3632 /* .pfnReserved0 = */ NULL,
3633 /* .pfnReserved1 = */ NULL,
3634 /* .pfnReserved2 = */ NULL,
3635 /* .pfnReserved3 = */ NULL,
3636 /* .pfnReserved4 = */ NULL,
3637 /* .pfnReserved5 = */ NULL,
3638 /* .pfnReserved6 = */ NULL,
3639 /* .pfnReserved7 = */ NULL,
3640#elif defined(IN_RC)
3641 /* .pfnConstruct = */ apicRZConstruct,
3642 /* .pfnReserved0 = */ NULL,
3643 /* .pfnReserved1 = */ NULL,
3644 /* .pfnReserved2 = */ NULL,
3645 /* .pfnReserved3 = */ NULL,
3646 /* .pfnReserved4 = */ NULL,
3647 /* .pfnReserved5 = */ NULL,
3648 /* .pfnReserved6 = */ NULL,
3649 /* .pfnReserved7 = */ NULL,
3650#else
3651# error "Not in IN_RING3, IN_RING0 or IN_RC!"
3652#endif
3653 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
3654};
3655
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