VirtualBox

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

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

APIC: When evaluating pending interrupts, do not take TPR into account, only the ISR. NEM/win depends on this behavior since it may need to get pending interrupts without an uptodate TPR (see bugref:9993).

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