VirtualBox

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

Last change on this file since 107361 was 107308, checked in by vboxsync, 2 months ago

VMM: bugref:10759 Refactor GIC for use with different backends.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.0 KB
Line 
1/* $Id: PDMAllApic.cpp 107308 2024-12-13 08:09:39Z 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 */
103VMM_INT_DECL(int) PDMApicSetTpr(PVMCPUCC pVCpu, uint8_t u8Tpr)
104{
105 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnSetTpr, VERR_INVALID_POINTER);
106 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnSetTpr(pVCpu, u8Tpr, false /* fForceX2ApicBehaviour */);
107}
108
109
110/**
111 * Returns whether the APIC hardware enabled or not.
112 *
113 * @returns @c true if enabled, @c false otherwise.
114 * @param pVCpu The cross context virtual CPU structure.
115 */
116VMM_INT_DECL(bool) PDMApicIsEnabled(PCVMCPUCC pVCpu)
117{
118 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnIsEnabled, false);
119 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnIsEnabled(pVCpu);
120}
121
122
123/**
124 * Reads an APIC MSR.
125 *
126 * @returns Strict VBox status code.
127 * @param pVCpu The cross context virtual CPU structure.
128 * @param u32Reg The MSR being read.
129 * @param pu64Value Where to store the read value.
130 */
131VMM_INT_DECL(VBOXSTRICTRC) PDMApicReadMsr(PVMCPUCC pVCpu, uint32_t u32Reg, uint64_t *pu64Value)
132{
133 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnReadMsr, VERR_INVALID_POINTER);
134 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnReadMsr(pVCpu, u32Reg, pu64Value);
135}
136
137
138/**
139 * Writes an APIC MSR.
140 *
141 * @returns Strict VBox status code.
142 * @param pVCpu The cross context virtual CPU structure.
143 * @param u32Reg The MSR being written.
144 * @param u64Value The value to write.
145 */
146VMM_INT_DECL(VBOXSTRICTRC) PDMApicWriteMsr(PVMCPUCC pVCpu, uint32_t u32Reg, uint64_t u64Value)
147{
148 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnWriteMsr, VERR_INVALID_POINTER);
149 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnWriteMsr(pVCpu, u32Reg, u64Value);
150}
151
152
153/**
154 * Gets the APIC timer frequency.
155 *
156 * @returns Strict VBox status code.
157 * @param pVM The cross context VM structure.
158 * @param pu64Value Where to store the timer frequency.
159 */
160VMM_INT_DECL(int) PDMApicGetTimerFreq(PVMCC pVM, uint64_t *pu64Value)
161{
162 AssertReturn(PDM_TO_APICBACKEND(pVM)->pfnGetTimerFreq, VERR_INVALID_POINTER);
163 return PDM_TO_APICBACKEND(pVM)->pfnGetTimerFreq(pVM, pu64Value);
164}
165
166
167/**
168 * Assert/de-assert the local APIC's LINT0/LINT1 interrupt pins.
169 *
170 * @returns Strict VBox status code.
171 * @param pVCpu The cross context virtual CPU structure.
172 * @param u8Pin The interrupt pin (0 for LINT0 or 1 for LINT1).
173 * @param u8Level The level (0 for low or 1 for high).
174 * @param rcRZ The return code if the operation cannot be performed in
175 * the current context.
176 *
177 * @note All callers totally ignores the status code!
178 */
179VMM_INT_DECL(VBOXSTRICTRC) PDMApicSetLocalInterrupt(PVMCPUCC pVCpu, uint8_t u8Pin, uint8_t u8Level, int rcRZ)
180{
181 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnSetLocalInterrupt, VERR_INVALID_POINTER);
182 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnSetLocalInterrupt(pVCpu, u8Pin, u8Level, rcRZ);
183}
184
185
186/**
187 * Gets the APIC base MSR (no checks are performed wrt APIC hardware or its
188 * state).
189 *
190 * @returns The base MSR value.
191 * @param pVCpu The cross context virtual CPU structure.
192 */
193VMM_INT_DECL(uint64_t) PDMApicGetBaseMsrNoCheck(PCVMCPUCC pVCpu)
194{
195 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnGetBaseMsrNoCheck, 0);
196 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnGetBaseMsrNoCheck(pVCpu);
197}
198
199
200/**
201 * Gets the APIC base MSR.
202 *
203 * @returns Strict VBox status code.
204 * @param pVCpu The cross context virtual CPU structure.
205 * @param pu64Value Where to store the MSR value.
206 */
207VMM_INT_DECL(VBOXSTRICTRC) PDMApicGetBaseMsr(PVMCPUCC pVCpu, uint64_t *pu64Value)
208{
209 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnGetBaseMsr, VERR_INVALID_POINTER);
210 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnGetBaseMsr(pVCpu, pu64Value);
211}
212
213
214/**
215 * Sets the APIC base MSR.
216 *
217 * @returns VBox status code - no informational ones, esp. not
218 * VINF_CPUM_R3_MSR_WRITE. Only the following two:
219 * @retval VINF_SUCCESS
220 * @retval VERR_CPUM_RAISE_GP_0
221 * @retval VERR_INVALID_POINTER
222 *
223 * @param pVCpu The cross context virtual CPU structure.
224 * @param u64BaseMsr The value to set.
225 */
226VMM_INT_DECL(int) PDMApicSetBaseMsr(PVMCPUCC pVCpu, uint64_t u64BaseMsr)
227{
228 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnSetBaseMsr, VERR_INVALID_POINTER);
229 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnSetBaseMsr(pVCpu, u64BaseMsr);
230}
231
232
233/**
234 * Gets the next highest-priority interrupt from the APIC, marking it as an
235 * "in-service" interrupt.
236 *
237 * @returns VBox status code.
238 * @param pVCpu The cross context virtual CPU structure.
239 * @param pu8Vector Where to store the vector.
240 * @param puSrcTag Where to store the interrupt source tag (debugging).
241 */
242VMM_INT_DECL(int) PDMApicGetInterrupt(PVMCPUCC pVCpu, uint8_t *pu8Vector, uint32_t *puSrcTag)
243{
244 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnGetInterrupt, VERR_INVALID_POINTER);
245 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnGetInterrupt(pVCpu, pu8Vector, puSrcTag);
246}
247
248
249/**
250 * Delivers an interrupt message via the system bus.
251 *
252 * @returns VBox status code.
253 * @param pVM The cross context VM structure.
254 * @param uDest The destination mask.
255 * @param uDestMode The destination mode.
256 * @param uDeliveryMode The delivery mode.
257 * @param uVector The interrupt vector.
258 * @param uPolarity The interrupt line polarity.
259 * @param uTriggerMode The trigger mode.
260 * @param uSrcTag The interrupt source tag (debugging).
261 */
262VMM_INT_DECL(int) PDMApicBusDeliver(PVMCC pVM, uint8_t uDest, uint8_t uDestMode, uint8_t uDeliveryMode, uint8_t uVector,
263 uint8_t uPolarity, uint8_t uTriggerMode, uint32_t uSrcTag)
264{
265 AssertReturn(PDM_TO_APICBACKEND(pVM)->pfnBusDeliver, VERR_INVALID_POINTER);
266 return PDM_TO_APICBACKEND(pVM)->pfnBusDeliver(pVM, uDest, uDestMode, uDeliveryMode, uVector, uPolarity, uTriggerMode,
267 uSrcTag);
268}
269
270
271#ifdef IN_RING0
272/**
273 * Gets the APIC page pointers for the specified VCPU.
274 *
275 * @returns VBox status code.
276 * @param pVCpu The cross context virtual CPU structure.
277 * @param pHCPhys Where to store the host-context physical address.
278 * @param pR0Ptr Where to store the ring-0 address.
279 * @param pR3Ptr Where to store the ring-3 address (optional).
280 */
281VMM_INT_DECL(int) PDMR0ApicGetApicPageForCpu(PCVMCPUCC pVCpu, PRTHCPHYS pHCPhys, PRTR0PTR pR0Ptr, PRTR3PTR pR3Ptr)
282{
283 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnGetApicPageForCpu, VERR_INVALID_POINTER);
284 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnGetApicPageForCpu(pVCpu, pHCPhys, pR0Ptr, pR3Ptr);
285}
286#endif /* IN_RING0 */
287
288
289#ifdef IN_RING3
290/**
291 * Sets whether Hyper-V compatibility mode (MSR interface) is enabled or not.
292 *
293 * This mode is a hybrid of xAPIC and x2APIC modes, some caveats:
294 * 1. MSRs are used even ones that are missing (illegal) in x2APIC like DFR.
295 * 2. A single ICR is used by the guest to send IPIs rather than 2 ICR writes.
296 * 3. It is unclear what the behaviour will be when invalid bits are set,
297 * currently we follow x2APIC behaviour of causing a \#GP.
298 *
299 * @returns VBox status code.
300 * @param pVM The cross context VM structure.
301 * @param fHyperVCompatMode Whether the compatibility mode is enabled.
302 */
303VMMR3_INT_DECL(int) PDMR3ApicHvSetCompatMode(PVM pVM, bool fHyperVCompatMode)
304{
305 AssertReturn(PDM_TO_APICBACKEND(pVM)->pfnHvSetCompatMode, VERR_INVALID_POINTER);
306 return PDM_TO_APICBACKEND(pVM)->pfnHvSetCompatMode(pVM, fHyperVCompatMode);
307}
308#endif /* IN_RING3 */
309
310
311/**
312 * Posts an interrupt to a target APIC.
313 * Paravirtualized Hyper-V interface.
314 *
315 * @param pVCpu The cross context virtual CPU structure.
316 * @param uVector The vector of the interrupt to be posted.
317 * @param fAutoEoi Whether this interrupt has automatic EOI
318 * treatment.
319 * @param enmTriggerMode The trigger mode of the interrupt.
320 *
321 * @thread Any.
322 */
323VMM_INT_DECL(void) PDMApicHvSendInterrupt(PVMCPUCC pVCpu, uint8_t uVector, bool fAutoEoi, XAPICTRIGGERMODE enmTriggerMode)
324{
325 AssertReturnVoid(PDMCPU_TO_APICBACKEND(pVCpu)->pfnPostInterrupt);
326 PDMCPU_TO_APICBACKEND(pVCpu)->pfnPostInterrupt(pVCpu, uVector, enmTriggerMode, fAutoEoi, 0 /* u32SrcTag */);
327}
328
329
330/**
331 * Sets the Task Priority Register (TPR).
332 * Paravirtualized Hyper-V interface.
333 *
334 * @returns Strict VBox status code.
335 * @param pVCpu The cross context virtual CPU structure.
336 * @param uTpr The TPR value to set.
337 *
338 * @remarks Validates like in x2APIC mode.
339 */
340VMM_INT_DECL(VBOXSTRICTRC) PDMApicHvSetTpr(PVMCPUCC pVCpu, uint8_t uTpr)
341{
342 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnSetTpr, VERR_INVALID_POINTER);
343 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnSetTpr(pVCpu, uTpr, true /* fForceX2ApicBehaviour */ );
344}
345
346
347/**
348 * Gets the Task Priority Register (TPR).
349 * Paravirtualized Hyper-V interface.
350 *
351 * @returns The TPR value.
352 * @param pVCpu The cross context virtual CPU structure.
353 */
354VMM_INT_DECL(uint8_t) PDMApicHvGetTpr(PVMCPUCC pVCpu)
355{
356 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnReadRaw32, 0);
357 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnReadRaw32(pVCpu, XAPIC_OFF_TPR);
358}
359
360
361/**
362 * Sets the Interrupt Command Register (ICR).
363 * Paravirtualized Hyper-V interface.
364 *
365 * @returns Strict VBox status code.
366 * @param pVCpu The cross context virtual CPU structure.
367 * @param uIcr The ICR value to set.
368 */
369VMM_INT_DECL(VBOXSTRICTRC) PDMApicHvSetIcr(PVMCPUCC pVCpu, uint64_t uIcr)
370{
371 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnSetIcr, VERR_INVALID_POINTER);
372 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnSetIcr(pVCpu, uIcr, VINF_CPUM_R3_MSR_WRITE);
373}
374
375
376/**
377 * Gets the Interrupt Command Register (ICR).
378 * Paravirtualized Hyper-V interface.
379 *
380 * @returns The ICR value.
381 * @param pVCpu The cross context virtual CPU structure.
382 */
383VMM_INT_DECL(uint64_t) PDMApicHvGetIcr(PVMCPUCC pVCpu)
384{
385 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnGetIcrNoCheck, 0);
386 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnGetIcrNoCheck(pVCpu);
387}
388
389
390/**
391 * Sets the End-Of-Interrupt (EOI) register.
392 * Paravirtualized Hyper-V interface.
393 *
394 * @returns Strict VBox status code.
395 * @param pVCpu The cross context virtual CPU structure.
396 * @param uEoi The EOI value.
397 */
398VMM_INT_DECL(VBOXSTRICTRC) PDMApicHvSetEoi(PVMCPUCC pVCpu, uint32_t uEoi)
399{
400 AssertReturn(PDMCPU_TO_APICBACKEND(pVCpu)->pfnSetEoi, VERR_INVALID_POINTER);
401 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnSetEoi(pVCpu, uEoi, true /* fForceX2ApicBehaviour */);
402}
403
404
405#ifdef IN_RING3
406/**
407 * Initializes per-VCPU APIC to the state following an INIT reset
408 * ("Wait-for-SIPI" state).
409 *
410 * @param pVCpu The cross context virtual CPU structure.
411 */
412VMMR3_INT_DECL(void) PDMR3ApicInitIpi(PVMCPU pVCpu)
413{
414 AssertReturnVoid(PDMCPU_TO_APICBACKEND(pVCpu)->pfnInitIpi);
415 return PDMCPU_TO_APICBACKEND(pVCpu)->pfnInitIpi(pVCpu);
416}
417#endif /* IN_RING3 */
418
419
420/**
421 * Registers a PDM APIC backend.
422 *
423 * @returns VBox status code.
424 * @param pVM The cross context VM structure.
425 * @param enmBackendType The PDM APIC backend type.
426 * @param pBackend The PDM APIC backend.
427 */
428VMM_INT_DECL(int) PDMApicRegisterBackend(PVMCC pVM, PDMAPICBACKENDTYPE enmBackendType, PCPDMAPICBACKEND pBackend)
429{
430 /*
431 * Validate.
432 */
433 AssertPtrReturn(pVM, VERR_INVALID_PARAMETER);
434 AssertPtrReturn(pBackend, VERR_INVALID_PARAMETER);
435 AssertReturn( enmBackendType > PDMAPICBACKENDTYPE_NONE
436 && enmBackendType < PDMAPICBACKENDTYPE_END, VERR_INVALID_PARAMETER);
437
438 AssertPtrReturn(pBackend->pfnIsEnabled, VERR_INVALID_POINTER);
439 AssertPtrReturn(pBackend->pfnInitIpi, VERR_INVALID_POINTER);
440 AssertPtrReturn(pBackend->pfnGetBaseMsrNoCheck, VERR_INVALID_POINTER);
441 AssertPtrReturn(pBackend->pfnGetBaseMsr, VERR_INVALID_POINTER);
442 AssertPtrReturn(pBackend->pfnSetBaseMsr, VERR_INVALID_POINTER);
443 AssertPtrReturn(pBackend->pfnReadRaw32, VERR_INVALID_POINTER);
444 AssertPtrReturn(pBackend->pfnReadMsr, VERR_INVALID_POINTER);
445 AssertPtrReturn(pBackend->pfnWriteMsr, VERR_INVALID_POINTER);
446 AssertPtrReturn(pBackend->pfnGetTpr, VERR_INVALID_POINTER);
447 AssertPtrReturn(pBackend->pfnSetTpr, VERR_INVALID_POINTER);
448 AssertPtrReturn(pBackend->pfnGetIcrNoCheck, VERR_INVALID_POINTER);
449 AssertPtrReturn(pBackend->pfnSetIcr, VERR_INVALID_POINTER);
450 AssertPtrReturn(pBackend->pfnGetTimerFreq, VERR_INVALID_POINTER);
451 AssertPtrReturn(pBackend->pfnSetLocalInterrupt, VERR_INVALID_POINTER);
452 AssertPtrReturn(pBackend->pfnGetInterrupt, VERR_INVALID_POINTER);
453 AssertPtrReturn(pBackend->pfnPostInterrupt, VERR_INVALID_POINTER);
454 AssertPtrReturn(pBackend->pfnUpdatePendingInterrupts, VERR_INVALID_POINTER);
455 AssertPtrReturn(pBackend->pfnBusDeliver, VERR_INVALID_POINTER);
456 AssertPtrReturn(pBackend->pfnSetEoi, VERR_INVALID_POINTER);
457#if defined(IN_RING3)
458 AssertPtrReturn(pBackend->pfnHvSetCompatMode, VERR_INVALID_POINTER);
459#elif defined(IN_RING0)
460 AssertPtrReturn(pBackend->pfnGetApicPageForCpu, VERR_INVALID_POINTER);
461#endif
462
463 /*
464 * Register the backend.
465 */
466 pVM->pdm.s.Ic.u.x86.enmKind = enmBackendType;
467#ifdef IN_RING3
468 pVM->pdm.s.Ic.u.x86.ApicBackend = *pBackend;
469#else
470 pVM->pdmr0.s.Ic.u.x86.ApicBackend = *pBackend;
471#endif
472
473#ifdef IN_RING3
474 LogRel(("PDM: %s APIC backend registered\n", PDMApicGetBackendName(enmBackendType)));
475#endif
476 return VINF_SUCCESS;
477}
478
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