VirtualBox

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

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

APIC: More statistics for certain registers.

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