VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/PDMAllApic.cpp@ 107113

Last change on this file since 107113 was 107113, checked in by vboxsync, 4 months ago

VMM: bugref:10759 Restructure the APIC to allow different backends to be used.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.1 KB
Line 
1/* $Id: PDMAllApic.cpp 107113 2024-11-22 10:48:00Z vboxsync $ */
2/** @file
3 * PDM - APIC (Advanced Programmable Interrupt Controller) Interface.
4 */
5
6/*
7 * Copyright (C) 2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_PDM_APIC
33#include "PDMInternal.h"
34#include <VBox/vmm/vm.h>
35#include <VBox/vmm/gvm.h>
36
37
38/**
39 * Gets the PDM APIC backend name.
40 *
41 * @returns The backend name.
42 * @param enmBackendType The PDM APIC backend type.
43 */
44VMM_INT_DECL(const char *) PDMApicGetBackendName(PDMAPICBACKENDTYPE enmBackendType)
45{
46 switch (enmBackendType)
47 {
48 case PDMAPICBACKENDTYPE_NONE: return "None";
49 case PDMAPICBACKENDTYPE_VBOX: return "VirtualBox";
50 case PDMAPICBACKENDTYPE_KVM: return "KVM";
51 case PDMAPICBACKENDTYPE_HYPERV: return "Hyper-V";
52 case PDMAPICBACKENDTYPE_HVF: return "Hypervisor.Framework";
53 default:
54 break;
55 }
56 return "Invalid";
57}
58
59
60/**
61 * Updates pending interrupts from the pending-interrupt bitmaps to the IRR.
62 *
63 * @param pVCpu The cross context virtual CPU structure.
64 *
65 * @note NEM/win is ASSUMING the an up to date TPR is not required here.
66 */
67VMM_INT_DECL(void) PDMApicUpdatePendingInterrupts(PVMCPUCC pVCpu)
68{
69 AssertReturnVoid(PDMCPU_TO_APICBACKEND(pVCpu)->pfnUpdatePendingInterrupts);
70 PDMCPU_TO_APICBACKEND(pVCpu)->pfnUpdatePendingInterrupts(pVCpu);
71}
72
73
74/**
75 * Gets the APIC TPR (Task Priority Register).
76 *
77 * @returns VBox status code.
78 * @param pVCpu The cross context virtual CPU structure.
79 * @param pu8Tpr Where to store the TPR.
80 * @param pfPending Where to store whether there is a pending interrupt
81 * (optional, can be NULL).
82 * @param pu8PendingIntr Where to store the highest-priority pending
83 * interrupt (optional, can be NULL).
84 */
85VMM_INT_DECL(int) PDMApicGetTpr(PCVMCPUCC pVCpu, uint8_t *pu8Tpr, bool *pfPending, uint8_t *pu8PendingIntr)
86{
87 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnGetTpr, VERR_INVALID_POINTER);
88 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnGetTpr(pVCpu, pu8Tpr, pfPending, pu8PendingIntr);
89}
90
91
92/**
93 * Sets the TPR (Task Priority Register).
94 *
95 * @retval VINF_SUCCESS
96 * @retval VERR_CPUM_RAISE_GP_0
97 * @retval VERR_PDM_NO_APIC_INSTANCE
98 * @retval VERR_INVALID_POINTER
99 *
100 * @param pVCpu The cross context virtual CPU structure.
101 * @param u8Tpr The TPR value to set.
102 * @param fForceX2ApicBehaviour Pretend the APIC is in x2APIC mode during
103 * this write.
104 */
105VMM_INT_DECL(int) PDMApicSetTpr(PVMCPUCC pVCpu, uint8_t u8Tpr)
106{
107 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnSetTpr, VERR_INVALID_POINTER);
108 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnSetTpr(pVCpu, u8Tpr, false /* fForceX2ApicBehaviour */);
109}
110
111
112/**
113 * Returns whether the APIC hardware enabled or not.
114 *
115 * @returns @c true if enabled, @c false otherwise.
116 * @param pVCpu The cross context virtual CPU structure.
117 */
118VMM_INT_DECL(bool) PDMApicIsEnabled(PCVMCPUCC pVCpu)
119{
120 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnIsEnabled, false);
121 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnIsEnabled(pVCpu);
122}
123
124
125/**
126 * Reads an APIC MSR.
127 *
128 * @returns Strict VBox status code.
129 * @param pVCpu The cross context virtual CPU structure.
130 * @param u32Reg The MSR being read.
131 * @param pu64Value Where to store the read value.
132 */
133VMM_INT_DECL(VBOXSTRICTRC) PDMApicReadMsr(PVMCPUCC pVCpu, uint32_t u32Reg, uint64_t *pu64Value)
134{
135 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnReadMsr, VERR_INVALID_POINTER);
136 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnReadMsr(pVCpu, u32Reg, pu64Value);
137}
138
139
140/**
141 * Writes an APIC MSR.
142 *
143 * @returns Strict VBox status code.
144 * @param pVCpu The cross context virtual CPU structure.
145 * @param u32Reg The MSR being written.
146 * @param u64Value The value to write.
147 */
148VMM_INT_DECL(VBOXSTRICTRC) PDMApicWriteMsr(PVMCPUCC pVCpu, uint32_t u32Reg, uint64_t u64Value)
149{
150 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnWriteMsr, VERR_INVALID_POINTER);
151 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnWriteMsr(pVCpu, u32Reg, u64Value);
152}
153
154
155/**
156 * Gets the APIC timer frequency.
157 *
158 * @returns Strict VBox status code.
159 * @param pVM The cross context VM structure.
160 * @param pu64Value Where to store the timer frequency.
161 */
162VMM_INT_DECL(int) PDMApicGetTimerFreq(PVMCC pVM, uint64_t *pu64Value)
163{
164 AssertReturn(PDM_TO_APICBACKEND(pVM)->pfnGetTimerFreq, VERR_INVALID_POINTER);
165 return PDM_TO_APICBACKEND(pVM)->pfnGetTimerFreq(pVM, pu64Value);
166}
167
168
169/**
170 * Assert/de-assert the local APIC's LINT0/LINT1 interrupt pins.
171 *
172 * @returns Strict VBox status code.
173 * @param pVCpu The cross context virtual CPU structure.
174 * @param u8Pin The interrupt pin (0 for LINT0 or 1 for LINT1).
175 * @param u8Level The level (0 for low or 1 for high).
176 * @param rcRZ The return code if the operation cannot be performed in
177 * the current context.
178 *
179 * @note All callers totally ignores the status code!
180 */
181VMM_INT_DECL(VBOXSTRICTRC) PDMApicSetLocalInterrupt(PVMCPUCC pVCpu, uint8_t u8Pin, uint8_t u8Level, int rcRZ)
182{
183 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnSetLocalInterrupt, VERR_INVALID_POINTER);
184 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnSetLocalInterrupt(pVCpu, u8Pin, u8Level, rcRZ);
185}
186
187
188/**
189 * Gets the APIC base MSR (no checks are performed wrt APIC hardware or its
190 * state).
191 *
192 * @returns The base MSR value.
193 * @param pVCpu The cross context virtual CPU structure.
194 */
195VMM_INT_DECL(uint64_t) PDMApicGetBaseMsrNoCheck(PCVMCPUCC pVCpu)
196{
197 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnGetBaseMsrNoCheck, 0);
198 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnGetBaseMsrNoCheck(pVCpu);
199}
200
201
202/**
203 * Gets the APIC base MSR.
204 *
205 * @returns Strict VBox status code.
206 * @param pVCpu The cross context virtual CPU structure.
207 * @param pu64Value Where to store the MSR value.
208 */
209VMM_INT_DECL(VBOXSTRICTRC) PDMApicGetBaseMsr(PVMCPUCC pVCpu, uint64_t *pu64Value)
210{
211 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnGetBaseMsr, VERR_INVALID_POINTER);
212 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnGetBaseMsr(pVCpu, pu64Value);
213}
214
215
216/**
217 * Sets the APIC base MSR.
218 *
219 * @returns VBox status code - no informational ones, esp. not
220 * VINF_CPUM_R3_MSR_WRITE. Only the following two:
221 * @retval VINF_SUCCESS
222 * @retval VERR_CPUM_RAISE_GP_0
223 * @retval VERR_INVALID_POINTER
224 *
225 * @param pVCpu The cross context virtual CPU structure.
226 * @param u64BaseMsr The value to set.
227 */
228VMM_INT_DECL(int) PDMApicSetBaseMsr(PVMCPUCC pVCpu, uint64_t u64BaseMsr)
229{
230 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnSetBaseMsr, VERR_INVALID_POINTER);
231 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnSetBaseMsr(pVCpu, u64BaseMsr);
232}
233
234
235/**
236 * Gets the next highest-priority interrupt from the APIC, marking it as an
237 * "in-service" interrupt.
238 *
239 * @returns VBox status code.
240 * @param pVCpu The cross context virtual CPU structure.
241 * @param pu8Vector Where to store the vector.
242 * @param puSrcTag Where to store the interrupt source tag (debugging).
243 */
244VMM_INT_DECL(int) PDMApicGetInterrupt(PVMCPUCC pVCpu, uint8_t *pu8Vector, uint32_t *pu32TagSrc)
245{
246 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnGetInterrupt, VERR_INVALID_POINTER);
247 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnGetInterrupt(pVCpu, pu8Vector, pu32TagSrc);
248}
249
250
251/**
252 * Delivers an interrupt message via the system bus.
253 *
254 * @returns VBox status code.
255 * @param pVM The cross context VM structure.
256 * @param uDest The destination mask.
257 * @param uDestMode The destination mode.
258 * @param uDeliveryMode The delivery mode.
259 * @param uVector The interrupt vector.
260 * @param uPolarity The interrupt line polarity.
261 * @param uTriggerMode The trigger mode.
262 * @param uSrcTag The interrupt source tag (debugging).
263 */
264VMM_INT_DECL(int) PDMApicBusDeliver(PVMCC pVM, uint8_t uDest, uint8_t uDestMode, uint8_t uDeliveryMode, uint8_t uVector,
265 uint8_t uPolarity, uint8_t uTriggerMode, uint32_t uTagSrc)
266{
267 AssertReturn(PDM_TO_APICBACKEND(pVM)->pfnBusDeliver, VERR_INVALID_POINTER);
268 return PDM_TO_APICBACKEND(pVM)->pfnBusDeliver(pVM, uDest, uDestMode, uDeliveryMode, uVector, uPolarity, uTriggerMode,
269 uTagSrc);
270}
271
272
273#ifdef IN_RING0
274/**
275 * Gets the APIC page pointers for the specified VCPU.
276 *
277 * @returns VBox status code.
278 * @param pVCpu The cross context virtual CPU structure.
279 * @param pHCPhys Where to store the host-context physical address.
280 * @param pR0Ptr Where to store the ring-0 address.
281 * @param pR3Ptr Where to store the ring-3 address (optional).
282 */
283VMM_INT_DECL(int) PDMR0ApicGetApicPageForCpu(PCVMCPUCC pVCpu, PRTHCPHYS pHCPhys, PRTR0PTR pR0Ptr, PRTR3PTR pR3Ptr)
284{
285 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnGetApicPageForCpu, VERR_INVALID_POINTER);
286 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnGetApicPageForCpu(pVCpu, pHCPhys, pR0Ptr, pR3Ptr);
287}
288#endif /* IN_RING0 */
289
290
291#ifdef IN_RING3
292/**
293 * Sets whether Hyper-V compatibility mode (MSR interface) is enabled or not.
294 *
295 * This mode is a hybrid of xAPIC and x2APIC modes, some caveats:
296 * 1. MSRs are used even ones that are missing (illegal) in x2APIC like DFR.
297 * 2. A single ICR is used by the guest to send IPIs rather than 2 ICR writes.
298 * 3. It is unclear what the behaviour will be when invalid bits are set,
299 * currently we follow x2APIC behaviour of causing a \#GP.
300 *
301 * @returns VBox status code.
302 * @param pVM The cross context VM structure.
303 * @param fHyperVCompatMode Whether the compatibility mode is enabled.
304 */
305VMMR3_INT_DECL(int) PDMR3ApicHvSetCompatMode(PVM pVM, bool fHyperVCompatMode)
306{
307 AssertReturn(PDM_TO_APICBACKEND(pVM)->pfnHvSetCompatMode, VERR_INVALID_POINTER);
308 return PDM_TO_APICBACKEND(pVM)->pfnHvSetCompatMode(pVM, fHyperVCompatMode);
309}
310#endif /* IN_RING3 */
311
312
313/**
314 * Posts an interrupt to a target APIC.
315 * Paravirtualized Hyper-V interface.
316 *
317 * @param pVCpu The cross context virtual CPU structure.
318 * @param uVector The vector of the interrupt to be posted.
319 * @param fAutoEoi Whether this interrupt has automatic EOI
320 * treatment.
321 * @param enmTriggerMode The trigger mode of the interrupt.
322 *
323 * @thread Any.
324 */
325VMM_INT_DECL(void) PDMApicHvSendInterrupt(PVMCPUCC pVCpu, uint8_t uVector, bool fAutoEoi, XAPICTRIGGERMODE enmTriggerMode)
326{
327 AssertReturnVoid(PDMCPU_TO_APICBACKEND(pVCpu)->pfnPostInterrupt);
328 PDMCPU_TO_APICBACKEND(pVCpu)->pfnPostInterrupt(pVCpu, uVector, enmTriggerMode, fAutoEoi, 0 /* u32SrcTag */);
329}
330
331
332/**
333 * Sets the Task Priority Register (TPR).
334 * Paravirtualized Hyper-V interface.
335 *
336 * @returns Strict VBox status code.
337 * @param pVCpu The cross context virtual CPU structure.
338 * @param uTpr The TPR value to set.
339 *
340 * @remarks Validates like in x2APIC mode.
341 */
342VMM_INT_DECL(VBOXSTRICTRC) PDMApicHvSetTpr(PVMCPUCC pVCpu, uint8_t uTpr)
343{
344 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnSetTpr, VERR_INVALID_POINTER);
345 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnSetTpr(pVCpu, uTpr, true /* fForceX2ApicBehaviour */ );
346}
347
348
349/**
350 * Gets the Task Priority Register (TPR).
351 * Paravirtualized Hyper-V interface.
352 *
353 * @returns The TPR value.
354 * @param pVCpu The cross context virtual CPU structure.
355 */
356VMM_INT_DECL(uint8_t) PDMApicHvGetTpr(PVMCPUCC pVCpu)
357{
358 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnReadRaw32, 0);
359 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnReadRaw32(pVCpu, XAPIC_OFF_TPR);
360}
361
362
363/**
364 * Sets the Interrupt Command Register (ICR).
365 * Paravirtualized Hyper-V interface.
366 *
367 * @returns Strict VBox status code.
368 * @param pVCpu The cross context virtual CPU structure.
369 * @param uIcr The ICR value to set.
370 */
371VMM_INT_DECL(VBOXSTRICTRC) PDMApicHvSetIcr(PVMCPUCC pVCpu, uint64_t uIcr)
372{
373 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnSetIcr, VERR_INVALID_POINTER);
374 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnSetIcr(pVCpu, uIcr, VINF_CPUM_R3_MSR_WRITE);
375}
376
377
378/**
379 * Gets the Interrupt Command Register (ICR).
380 * Paravirtualized Hyper-V interface.
381 *
382 * @returns The ICR value.
383 * @param pVCpu The cross context virtual CPU structure.
384 */
385VMM_INT_DECL(uint64_t) PDMApicHvGetIcr(PVMCPUCC pVCpu)
386{
387 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnGetIcrNoCheck, 0);
388 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnGetIcrNoCheck(pVCpu);
389}
390
391
392/**
393 * Sets the End-Of-Interrupt (EOI) register.
394 * Paravirtualized Hyper-V interface.
395 *
396 * @returns Strict VBox status code.
397 * @param pVCpu The cross context virtual CPU structure.
398 * @param uEoi The EOI value.
399 */
400VMM_INT_DECL(VBOXSTRICTRC) PDMApicHvSetEoi(PVMCPUCC pVCpu, uint32_t uEoi)
401{
402 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnSetEoi, VERR_INVALID_POINTER);
403 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnSetEoi(pVCpu, uEoi, true /* fForceX2ApicBehaviour */);
404}
405
406
407#ifdef IN_RING3
408/**
409 * Initializes per-VCPU APIC to the state following an INIT reset
410 * ("Wait-for-SIPI" state).
411 *
412 * @param pVCpu The cross context virtual CPU structure.
413 */
414VMMR3_INT_DECL(void) PDMR3ApicInitIpi(PVMCPU pVCpu)
415{
416 AssertReturnVoid(PDMCPU_TO_APICBACKEND(pVCpu)->pfnInitIpi);
417 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnInitIpi(pVCpu);
418}
419#endif /* IN_RING3 */
420
421
422/**
423 * Registers a PDM APIC backend.
424 *
425 * @returns VBox status code.
426 * @param pVM The cross context VM structure.
427 * @param enmBackendType The PDM APIC backend type.
428 * @param pBackend The PDM APIC backend.
429 */
430VMM_INT_DECL(int) PDMApicRegisterBackend(PVMCC pVM, PDMAPICBACKENDTYPE enmBackendType, PCPDMAPICBACKEND pBackend)
431{
432 /*
433 * Validate.
434 */
435 AssertPtrReturn(pVM, VERR_INVALID_PARAMETER);
436 AssertPtrReturn(pBackend, VERR_INVALID_PARAMETER);
437 AssertReturn( enmBackendType > PDMAPICBACKENDTYPE_NONE
438 && enmBackendType < PDMAPICBACKENDTYPE_END, VERR_INVALID_PARAMETER);
439
440 AssertPtrReturn(pBackend->pfnIsEnabled, VERR_INVALID_POINTER);
441 AssertPtrReturn(pBackend->pfnInitIpi, VERR_INVALID_POINTER);
442 AssertPtrReturn(pBackend->pfnGetBaseMsrNoCheck, VERR_INVALID_POINTER);
443 AssertPtrReturn(pBackend->pfnGetBaseMsr, VERR_INVALID_POINTER);
444 AssertPtrReturn(pBackend->pfnSetBaseMsr, VERR_INVALID_POINTER);
445 AssertPtrReturn(pBackend->pfnReadRaw32, VERR_INVALID_POINTER);
446 AssertPtrReturn(pBackend->pfnReadMsr, VERR_INVALID_POINTER);
447 AssertPtrReturn(pBackend->pfnWriteMsr, VERR_INVALID_POINTER);
448 AssertPtrReturn(pBackend->pfnGetTpr, VERR_INVALID_POINTER);
449 AssertPtrReturn(pBackend->pfnSetTpr, VERR_INVALID_POINTER);
450 AssertPtrReturn(pBackend->pfnGetIcrNoCheck, VERR_INVALID_POINTER);
451 AssertPtrReturn(pBackend->pfnSetIcr, VERR_INVALID_POINTER);
452 AssertPtrReturn(pBackend->pfnGetTimerFreq, VERR_INVALID_POINTER);
453 AssertPtrReturn(pBackend->pfnSetLocalInterrupt, VERR_INVALID_POINTER);
454 AssertPtrReturn(pBackend->pfnGetInterrupt, VERR_INVALID_POINTER);
455 AssertPtrReturn(pBackend->pfnPostInterrupt, VERR_INVALID_POINTER);
456 AssertPtrReturn(pBackend->pfnUpdatePendingInterrupts, VERR_INVALID_POINTER);
457 AssertPtrReturn(pBackend->pfnBusDeliver, VERR_INVALID_POINTER);
458 AssertPtrReturn(pBackend->pfnSetEoi, VERR_INVALID_POINTER);
459#if defined(IN_RING3)
460 AssertPtrReturn(pBackend->pfnHvSetCompatMode, VERR_INVALID_POINTER);
461#elif defined(IN_RING0)
462 AssertPtrReturn(pBackend->pfnGetApicPageForCpu, VERR_INVALID_POINTER);
463#endif
464
465 /*
466 * Register the backend.
467 */
468 pVM->pdm.s.Ic.enmKind = enmBackendType;
469#ifdef IN_RING3
470 pVM->pdm.s.Ic.ApicBackend = *pBackend;
471#else
472 pVM->pdmr0.s.Ic.ApicBackend = *pBackend;
473#endif
474
475#ifdef IN_RING3
476 LogRel(("PDM: %s APIC backend registered\n", PDMApicGetBackendName(enmBackendType)));
477#endif
478 return VINF_SUCCESS;
479}
480
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette