VirtualBox

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

Last change on this file since 93115 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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