VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/APICR3Nem-win.cpp

Last change on this file was 108435, checked in by vboxsync, 10 days ago

VMM/NEM/Hyper-V: Started implementing a NEM/Hyper-V specific APIC emulation utilizing the LocalApicEmulation feature of Hyper-V, bugref:9993 [scm]

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 24.5 KB
Line 
1/* $Id: APICR3Nem-win.cpp 108435 2025-03-04 11:27:15Z vboxsync $ */
2/** @file
3 * GIC - Generic Interrupt Controller Architecture (GIC) - Hyper-V 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_DEV_APIC
33#include <iprt/nt/nt-and-windows.h>
34#include <iprt/nt/hyperv.h>
35#include <iprt/mem.h>
36#include <WinHvPlatform.h>
37
38#include <VBox/log.h>
39#include "APICInternal.h"
40#include "NEMInternal.h" /* Need access to the partition handle. */
41#include <VBox/vmm/pdmapic.h>
42#include <VBox/vmm/cpum.h>
43#include <VBox/vmm/hm.h>
44#include <VBox/vmm/mm.h>
45#include <VBox/vmm/pdmdev.h>
46#include <VBox/vmm/ssm.h>
47#include <VBox/vmm/vm.h>
48
49#ifndef VBOX_DEVICE_STRUCT_TESTCASE
50
51
52/*********************************************************************************************************************************
53* Defined Constants And Macros *
54*********************************************************************************************************************************/
55
56
57/*********************************************************************************************************************************
58* Structures and Typedefs *
59*********************************************************************************************************************************/
60
61/**
62 * APICHv PDM instance data (per-VM).
63 */
64typedef struct APICHVDEV
65{
66 /** Pointer to the PDM device instance. */
67 PPDMDEVINSR3 pDevIns;
68 /** The partition handle grabbed from NEM. */
69 WHV_PARTITION_HANDLE hPartition;
70 /** Cached TPR value. */
71 uint8_t bTpr;
72} APICHVDEV;
73/** Pointer to a APIC Hyper-V device. */
74typedef APICHVDEV *PAPICHVDEV;
75/** Pointer to a const APIC Hyper-V device. */
76typedef APICHVDEV const *PCAPICHVDEV;
77
78
79/*********************************************************************************************************************************
80* Global Variables *
81*********************************************************************************************************************************/
82extern decltype(WHvGetVirtualProcessorState) *g_pfnWHvGetVirtualProcessorState;
83extern decltype(WHvSetVirtualProcessorState) *g_pfnWHvSetVirtualProcessorState;
84extern decltype(WHvGetVirtualProcessorInterruptControllerState2) *g_pfnWHvGetVirtualProcessorInterruptControllerState2;
85extern decltype(WHvSetVirtualProcessorInterruptControllerState2) *g_pfnWHvSetVirtualProcessorInterruptControllerState2;
86extern decltype(WHvRequestInterrupt) *g_pfnWHvRequestInterrupt;
87
88/*
89 * Let the preprocessor alias the APIs to import variables for better autocompletion.
90 */
91#ifndef IN_SLICKEDIT
92# define WHvGetVirtualProcessorState g_pfnWHvGetVirtualProcessorState
93# define WHvSetVirtualProcessorState g_pfnWHvSetVirtualProcessorState
94# define WHvGetVirtualProcessorInterruptControllerState2 g_pfnWHvGetVirtualProcessorInterruptControllerState2
95# define WHvSetVirtualProcessorInterruptControllerState2 g_pfnWHvSetVirtualProcessorInterruptControllerState2
96# define WHvRequestInterrupt g_pfnWHvRequestInterrupt
97#endif
98
99
100/**
101 * @interface_method_impl{PDMAPICBACKEND,pfnIsEnabled}
102 */
103static DECLCALLBACK(bool) apicR3HvIsEnabled(PCVMCPUCC pVCpu)
104{
105 /*
106 * We should never end up here as this is called only from the VMX and SVM
107 * code in R0 which we don't run if this is active.
108 */
109 RT_NOREF(pVCpu);
110 AssertFailedReturn(false);
111}
112
113
114/**
115 * @interface_method_impl{PDMAPICBACKEND,pfnInitIpi}
116 */
117static DECLCALLBACK(void) apicR3HvInitIpi(PVMCPUCC pVCpu)
118{
119 VMCPU_ASSERT_EMT_OR_NOT_RUNNING(pVCpu);
120 PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
121
122 /*
123 * See Intel spec. 10.4.7.3 "Local APIC State After an INIT Reset (Wait-for-SIPI State)"
124 * and AMD spec 16.3.2 "APIC Registers".
125 *
126 * The reason we don't simply zero out the entire APIC page and only set the non-zero members
127 * is because there are some registers that are not touched by the INIT IPI (e.g. version)
128 * operation and this function is only a subset of the reset operation.
129 */
130 RT_ZERO(pXApicPage->irr);
131 RT_ZERO(pXApicPage->irr);
132 RT_ZERO(pXApicPage->isr);
133 RT_ZERO(pXApicPage->tmr);
134 RT_ZERO(pXApicPage->icr_hi);
135 RT_ZERO(pXApicPage->icr_lo);
136 RT_ZERO(pXApicPage->ldr);
137 RT_ZERO(pXApicPage->tpr);
138 RT_ZERO(pXApicPage->ppr);
139 RT_ZERO(pXApicPage->timer_icr);
140 RT_ZERO(pXApicPage->timer_ccr);
141 RT_ZERO(pXApicPage->timer_dcr);
142
143 pXApicPage->dfr.u.u4Model = XAPICDESTFORMAT_FLAT;
144 pXApicPage->dfr.u.u28ReservedMb1 = UINT32_C(0xfffffff);
145
146 /** @todo CMCI. */
147
148 RT_ZERO(pXApicPage->lvt_timer);
149 pXApicPage->lvt_timer.u.u1Mask = 1;
150
151#if XAPIC_HARDWARE_VERSION == XAPIC_HARDWARE_VERSION_P4
152 RT_ZERO(pXApicPage->lvt_thermal);
153 pXApicPage->lvt_thermal.u.u1Mask = 1;
154#endif
155
156 RT_ZERO(pXApicPage->lvt_perf);
157 pXApicPage->lvt_perf.u.u1Mask = 1;
158
159 RT_ZERO(pXApicPage->lvt_lint0);
160 pXApicPage->lvt_lint0.u.u1Mask = 1;
161
162 RT_ZERO(pXApicPage->lvt_lint1);
163 pXApicPage->lvt_lint1.u.u1Mask = 1;
164
165 RT_ZERO(pXApicPage->lvt_error);
166 pXApicPage->lvt_error.u.u1Mask = 1;
167
168 RT_ZERO(pXApicPage->svr);
169 pXApicPage->svr.u.u8SpuriousVector = 0xff;
170
171 /* The self-IPI register is reset to 0. See Intel spec. 10.12.5.1 "x2APIC States" */
172 PX2APICPAGE pX2ApicPage = VMCPU_TO_X2APICPAGE(pVCpu);
173 RT_ZERO(pX2ApicPage->self_ipi);
174
175 /* Clear the pending-interrupt bitmaps. */
176 PAPICCPU pApicCpu = VMCPU_TO_APICCPU(pVCpu);
177#if 0
178 RT_BZERO(&pApicCpu->ApicPibLevel, sizeof(APICPIB));
179 RT_BZERO(pApicCpu->CTX_SUFF(pvApicPib), sizeof(APICPIB));
180#endif
181
182 /* Clear the interrupt line states for LINT0 and LINT1 pins. */
183 pApicCpu->fActiveLint0 = false;
184 pApicCpu->fActiveLint1 = false;
185}
186
187
188/**
189 * @interface_method_impl{PDMAPICBACKEND,pfnSetBaseMsr}
190 */
191static DECLCALLBACK(int) apicR3HvSetBaseMsr(PVMCPUCC pVCpu, uint64_t u64BaseMsr)
192{
193 RT_NOREF(pVCpu, u64BaseMsr);
194 AssertFailed();
195 return VINF_SUCCESS;
196}
197
198
199/**
200 * @interface_method_impl{PDMAPICBACKEND,pfnGetBaseMsrNoCheck}
201 */
202static DECLCALLBACK(uint64_t) apicR3HvGetBaseMsrNoCheck(PCVMCPUCC pVCpu)
203{
204 VMCPU_ASSERT_EMT_OR_NOT_RUNNING(pVCpu);
205 PCAPICCPU pApicCpu = VMCPU_TO_APICCPU(pVCpu);
206 return pApicCpu->uApicBaseMsr;
207}
208
209
210/**
211 * @interface_method_impl{PDMAPICBACKEND,pfnGetBaseMsr}
212 */
213static DECLCALLBACK(VBOXSTRICTRC) apicR3HvGetBaseMsr(PVMCPUCC pVCpu, uint64_t *pu64Value)
214{
215 VMCPU_ASSERT_EMT_OR_NOT_RUNNING(pVCpu);
216
217 PCAPIC pApic = VM_TO_APIC(pVCpu->CTX_SUFF(pVM));
218 if (pApic->enmMaxMode != PDMAPICMODE_NONE)
219 {
220 *pu64Value = apicR3HvGetBaseMsrNoCheck(pVCpu);
221 return VINF_SUCCESS;
222 }
223
224 if (pVCpu->apic.s.cLogMaxGetApicBaseAddr++ < 5)
225 LogRel(("APIC%u: Reading APIC base MSR (%#x) when there is no APIC -> #GP(0)\n", pVCpu->idCpu, MSR_IA32_APICBASE));
226 return VERR_CPUM_RAISE_GP_0;
227}
228
229
230/**
231 * @interface_method_impl{PDMAPICBACKEND,pfnReadRaw32}
232 */
233static DECLCALLBACK(uint32_t) apicR3HvReadRaw32(PCVMCPUCC pVCpu, uint16_t offReg)
234{
235 RT_NOREF(pVCpu, offReg);
236 AssertFailed();
237 return 0;
238}
239
240
241/**
242 * @interface_method_impl{PDMAPICBACKEND,pfnReadMsr}
243 */
244static DECLCALLBACK(VBOXSTRICTRC) apicR3HvReadMsr(PVMCPUCC pVCpu, uint32_t u32Reg, uint64_t *pu64Value)
245{
246 /*
247 * Validate.
248 */
249 VMCPU_ASSERT_EMT(pVCpu);
250 Assert(u32Reg >= MSR_IA32_X2APIC_ID && u32Reg <= MSR_IA32_X2APIC_SELF_IPI);
251 Assert(pu64Value);
252
253 RT_NOREF(pVCpu, u32Reg, pu64Value);
254 AssertFailed();
255 return VINF_SUCCESS;
256}
257
258
259/**
260 * @interface_method_impl{PDMAPICBACKEND,pfnWriteMsr}
261 */
262static DECLCALLBACK(VBOXSTRICTRC) apicR3HvWriteMsr(PVMCPUCC pVCpu, uint32_t u32Reg, uint64_t u64Value)
263{
264 /*
265 * Validate.
266 */
267 VMCPU_ASSERT_EMT(pVCpu);
268 Assert(u32Reg >= MSR_IA32_X2APIC_ID && u32Reg <= MSR_IA32_X2APIC_SELF_IPI);
269
270 RT_NOREF(pVCpu, u32Reg, u64Value);
271 AssertFailed();
272 return VINF_SUCCESS;
273}
274
275
276/**
277 * @interface_method_impl{PDMAPICBACKEND,pfnSetTpr}
278 */
279static DECLCALLBACK(int) apicR3HvSetTpr(PVMCPUCC pVCpu, uint8_t u8Tpr, bool fForceX2ApicBehaviour)
280{
281 RT_NOREF(fForceX2ApicBehaviour);
282 pVCpu->nem.s.bTpr = u8Tpr;
283 return VINF_SUCCESS;
284}
285
286
287/**
288 * @interface_method_impl{PDMAPICBACKEND,pfnGetTpr}
289 */
290static DECLCALLBACK(int) apicR3HvGetTpr(PCVMCPUCC pVCpu, uint8_t *pu8Tpr, bool *pfPending, uint8_t *pu8PendingIntr)
291{
292 VMCPU_ASSERT_EMT(pVCpu);
293
294 RT_NOREF(pfPending, pu8PendingIntr);
295 *pu8Tpr = pVCpu->nem.s.bTpr;
296 return VINF_SUCCESS;
297}
298
299
300/**
301 * @interface_method_impl{PDMAPICBACKEND,pfnGetIcrNoCheck}
302 */
303static DECLCALLBACK(uint64_t) apicR3HvGetIcrNoCheck(PVMCPUCC pVCpu)
304{
305 RT_NOREF(pVCpu);
306 AssertFailed();
307 return 0;
308}
309
310
311/**
312 * @interface_method_impl{PDMAPICBACKEND,pfnSetIcr}
313 */
314static DECLCALLBACK(VBOXSTRICTRC) apicR3HvSetIcr(PVMCPUCC pVCpu, uint64_t u64Icr, int rcRZ)
315{
316 VMCPU_ASSERT_EMT(pVCpu);
317
318 RT_NOREF(pVCpu, u64Icr, rcRZ);
319 AssertFailed();
320 return VINF_SUCCESS;
321}
322
323
324/**
325 * @interface_method_impl{PDMAPICBACKEND,pfnGetTimerFreq}
326 */
327static DECLCALLBACK(int) apicR3HvGetTimerFreq(PVMCC pVM, uint64_t *pu64Value)
328{
329 /*
330 * Validate.
331 */
332 Assert(pVM);
333 AssertPtrReturn(pu64Value, VERR_INVALID_PARAMETER);
334
335 RT_NOREF(pVM, pu64Value);
336 AssertFailed();
337 return VERR_PDM_NO_APIC_INSTANCE;
338}
339
340
341/**
342 * @interface_method_impl{PDMAPICBACKEND,pfnSetLocalInterrupt}
343 */
344static DECLCALLBACK(VBOXSTRICTRC) apicR3HvSetLocalInterrupt(PVMCPUCC pVCpu, uint8_t u8Pin, uint8_t u8Level, int rcRZ)
345{
346 AssertReturn(u8Pin <= 1, VERR_INVALID_PARAMETER);
347 AssertReturn(u8Level <= 1, VERR_INVALID_PARAMETER);
348
349 RT_NOREF(rcRZ);
350 /* The rest is handled in the NEM backend. */
351 if (u8Level)
352 VMCPU_FF_SET(pVCpu, VMCPU_FF_INTERRUPT_PIC);
353 else
354 VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_PIC);
355 return VINF_SUCCESS;
356}
357
358
359/**
360 * @interface_method_impl{PDMAPICBACKEND,pfnGetInterrupt}
361 */
362static DECLCALLBACK(int) apicR3HvGetInterrupt(PVMCPUCC pVCpu, uint8_t *pu8Vector, uint32_t *puSrcTag)
363{
364 VMCPU_ASSERT_EMT(pVCpu);
365 Assert(pu8Vector);
366
367 RT_NOREF(pVCpu, pu8Vector, puSrcTag);
368 AssertFailed();
369 return VERR_APIC_INTR_NOT_PENDING;
370}
371
372
373/**
374 * @interface_method_impl{PDMAPICBACKEND,pfnPostInterrupt}
375 */
376static DECLCALLBACK(bool) apicR3HvPostInterrupt(PVMCPUCC pVCpu, uint8_t uVector, XAPICTRIGGERMODE enmTriggerMode, bool fAutoEoi,
377 uint32_t uSrcTag)
378{
379 Assert(pVCpu);
380 Assert(uVector > XAPIC_ILLEGAL_VECTOR_END);
381 RT_NOREF(fAutoEoi);
382
383 RT_NOREF(pVCpu, uVector, enmTriggerMode, fAutoEoi, uSrcTag);
384 AssertFailed();
385 return false;
386}
387
388
389/**
390 * @interface_method_impl{PDMAPICBACKEND,pfnUpdatePendingInterrupts}
391 */
392static DECLCALLBACK(void) apicR3HvUpdatePendingInterrupts(PVMCPUCC pVCpu)
393{
394 VMCPU_ASSERT_EMT_OR_NOT_RUNNING(pVCpu);
395 RT_NOREF(pVCpu);
396 AssertFailed();
397}
398
399
400/**
401 * @interface_method_impl{PDMAPICBACKEND,pfnBusDeliver}
402 */
403static DECLCALLBACK(int) apicR3HvBusDeliver(PVMCC pVM, uint8_t uDest, uint8_t uDestMode, uint8_t uDeliveryMode, uint8_t uVector,
404 uint8_t uPolarity, uint8_t uTriggerMode, uint32_t uSrcTag)
405{
406 RT_NOREF(uPolarity, uSrcTag);
407
408 Assert(pVM->nem.s.fLocalApicEmulation);
409
410 WHV_INTERRUPT_CONTROL Control; RT_ZERO(Control);
411 Control.Type = uDeliveryMode; /* Matching up. */
412 Control.DestinationMode = uDestMode;
413 Control.TriggerMode = uTriggerMode;
414 Control.Destination = uDest;
415 Control.Vector = uVector;
416
417 HRESULT hrc = WHvRequestInterrupt(pVM->nem.s.hPartition, &Control, sizeof(Control));
418 if (FAILED(hrc))
419 {
420 LogRelMax(10, ("APIC/WHv: Delivering interrupt failed: %Rhrc (Last=%#x/%u)",
421 hrc, RTNtLastStatusValue(), RTNtLastErrorValue()));
422 return VERR_APIC_INTR_DISCARDED;
423 }
424
425 return VINF_SUCCESS;
426}
427
428
429/**
430 * @interface_method_impl{PDMAPICBACKEND,pfnSetEoi}
431 */
432static DECLCALLBACK(VBOXSTRICTRC) apicR3HvSetEoi(PVMCPUCC pVCpu, uint32_t uEoi, bool fForceX2ApicBehaviour)
433{
434 VMCPU_ASSERT_EMT(pVCpu);
435
436 RT_NOREF(pVCpu, uEoi, fForceX2ApicBehaviour);
437 AssertFailed();
438 return VINF_SUCCESS;
439}
440
441
442/**
443 * @interface_method_impl{PDMAPICBACKEND,pfnHvSetCompatMode}
444 */
445static DECLCALLBACK(int) apicR3NemHvSetCompatMode(PVM pVM, bool fHyperVCompatMode)
446{
447 RT_NOREF(pVM, fHyperVCompatMode);
448 //AssertFailed();
449 return VINF_SUCCESS;
450}
451
452
453/**
454 * Resets the APIC base MSR.
455 *
456 * @param pVCpu The cross context virtual CPU structure.
457 */
458static void apicResetBaseMsr(PVMCPUCC pVCpu)
459{
460 /*
461 * Initialize the APIC base MSR. The APIC enable-bit is set upon power-up or reset[1].
462 *
463 * A Reset (in xAPIC and x2APIC mode) brings up the local APIC in xAPIC mode.
464 * An INIT IPI does -not- cause a transition between xAPIC and x2APIC mode[2].
465 *
466 * [1] See AMD spec. 14.1.3 "Processor Initialization State"
467 * [2] See Intel spec. 10.12.5.1 "x2APIC States".
468 */
469 VMCPU_ASSERT_EMT_OR_NOT_RUNNING(pVCpu);
470
471 /* Construct. */
472 PAPICCPU pApicCpu = VMCPU_TO_APICCPU(pVCpu);
473 PAPIC pApic = VM_TO_APIC(pVCpu->CTX_SUFF(pVM));
474 uint64_t uApicBaseMsr = MSR_IA32_APICBASE_ADDR;
475 if (pVCpu->idCpu == 0)
476 uApicBaseMsr |= MSR_IA32_APICBASE_BSP;
477
478 /* If the VM was configured with no APIC, don't enable xAPIC mode, obviously. */
479 if (pApic->enmMaxMode != PDMAPICMODE_NONE)
480 {
481 uApicBaseMsr |= MSR_IA32_APICBASE_EN;
482
483 /*
484 * While coming out of a reset the APIC is enabled and in xAPIC mode. If software had previously
485 * disabled the APIC (which results in the CPUID bit being cleared as well) we re-enable it here.
486 * See Intel spec. 10.12.5.1 "x2APIC States".
487 */
488 if (CPUMSetGuestCpuIdPerCpuApicFeature(pVCpu, true /*fVisible*/) == false)
489 LogRel(("APIC%u: Resetting mode to xAPIC\n", pVCpu->idCpu));
490 }
491
492 /* Commit. */
493 ASMAtomicWriteU64(&pApicCpu->uApicBaseMsr, uApicBaseMsr);
494}
495
496
497/**
498 * Initializes per-VCPU APIC to the state following a power-up or hardware
499 * reset.
500 *
501 * @param pVCpu The cross context virtual CPU structure.
502 * @param fResetApicBaseMsr Whether to reset the APIC base MSR.
503 */
504static void apicR3HvResetCpu(PVMCPUCC pVCpu, bool fResetApicBaseMsr)
505{
506 VMCPU_ASSERT_EMT_OR_NOT_RUNNING(pVCpu);
507
508 LogFlow(("APIC%u: apicR3ResetCpu: fResetApicBaseMsr=%RTbool\n", pVCpu->idCpu, fResetApicBaseMsr));
509
510#ifdef VBOX_STRICT
511 /* Verify that the initial APIC ID reported via CPUID matches our VMCPU ID assumption. */
512 uint32_t uEax, uEbx, uEcx, uEdx;
513 uEax = uEbx = uEcx = uEdx = UINT32_MAX;
514 CPUMGetGuestCpuId(pVCpu, 1, 0, -1 /*f64BitMode*/, &uEax, &uEbx, &uEcx, &uEdx);
515 Assert(((uEbx >> 24) & 0xff) == pVCpu->idCpu);
516#endif
517
518 /*
519 * The state following a power-up or reset is a superset of the INIT state.
520 * See Intel spec. 10.4.7.3 "Local APIC State After an INIT Reset ('Wait-for-SIPI' State)"
521 */
522 apicR3HvInitIpi(pVCpu);
523
524 /*
525 * The APIC version register is read-only, so just initialize it here.
526 * It is not clear from the specs, where exactly it is initialized.
527 * The version determines the number of LVT entries and size of the APIC ID (8 bits for P4).
528 */
529 PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
530#if XAPIC_HARDWARE_VERSION == XAPIC_HARDWARE_VERSION_P4
531 pXApicPage->version.u.u8MaxLvtEntry = XAPIC_MAX_LVT_ENTRIES_P4 - 1;
532 pXApicPage->version.u.u8Version = XAPIC_HARDWARE_VERSION_P4;
533 AssertCompile(sizeof(pXApicPage->id.u8ApicId) >= XAPIC_APIC_ID_BIT_COUNT_P4 / 8);
534#else
535# error "Implement Pentium and P6 family APIC architectures"
536#endif
537
538 /** @todo It isn't clear in the spec. where exactly the default base address
539 * is (re)initialized, atm we do it here in Reset. */
540 if (fResetApicBaseMsr)
541 apicResetBaseMsr(pVCpu);
542
543 /*
544 * Initialize the APIC ID register to xAPIC format.
545 */
546 RT_BZERO(&pXApicPage->id, sizeof(pXApicPage->id));
547 pXApicPage->id.u8ApicId = pVCpu->idCpu;
548}
549
550
551/**
552 * @interface_method_impl{PDMDEVREG,pfnReset}
553 */
554DECLCALLBACK(void) apicR3HvReset(PPDMDEVINS pDevIns)
555{
556 PVM pVM = PDMDevHlpGetVM(pDevIns);
557 VM_ASSERT_EMT0(pVM);
558 VM_ASSERT_IS_NOT_RUNNING(pVM);
559
560 for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
561 {
562 PVMCPU pVCpuDest = pVM->apCpusR3[idCpu];
563 PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpuDest);
564
565 apicR3HvResetCpu(pVCpuDest, true /*fResetApicBaseMsr*/);
566
567 HRESULT hrc;
568 if (WHvSetVirtualProcessorState)
569 hrc = WHvSetVirtualProcessorState(pVM->nem.s.hPartition, idCpu, WHvVirtualProcessorStateTypeInterruptControllerState2,
570 pXApicPage, sizeof(*pXApicPage));
571 else
572 hrc = WHvSetVirtualProcessorInterruptControllerState2(pVM->nem.s.hPartition, idCpu, pXApicPage, sizeof(*pXApicPage));
573 AssertRelease(SUCCEEDED(hrc));
574 AssertRelease(SUCCEEDED(hrc));
575 }
576
577 LogFlow(("GIC: gicR3HvReset\n"));
578}
579
580
581/**
582 * @interface_method_impl{PDMDEVREG,pfnDestruct}
583 */
584DECLCALLBACK(int) apicR3HvDestruct(PPDMDEVINS pDevIns)
585{
586 LogFlowFunc(("pDevIns=%p\n", pDevIns));
587 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
588
589 return VINF_SUCCESS;
590}
591
592
593/**
594 * @interface_method_impl{PDMDEVREG,pfnConstruct}
595 */
596DECLCALLBACK(int) apicR3HvConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
597{
598 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
599 PAPICHVDEV pThis = PDMDEVINS_2_DATA(pDevIns, PAPICHVDEV);
600 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
601 PVM pVM = PDMDevHlpGetVM(pDevIns);
602 Assert(iInstance == 0); NOREF(iInstance);
603
604 RT_NOREF(pCfg, pHlp);
605
606 /*
607 * Init the data.
608 */
609 //pGic->pDevInsR3 = pDevIns;
610 pThis->pDevIns = pDevIns;
611 pThis->hPartition = pVM->nem.s.hPartition;
612
613 /*
614 * Validate GIC settings.
615 */
616 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "Mode|IOAPIC|NumCPUs|MacOSWorkaround", "");
617
618 /*
619 * Disable automatic PDM locking for this device.
620 */
621 int rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
622 AssertRCReturn(rc, rc);
623
624 /*
625 * Register the APIC with PDM.
626 */
627 rc = PDMDevHlpIcRegister(pDevIns);
628 AssertLogRelRCReturn(rc, rc);
629
630 rc = PDMApicRegisterBackend(pVM, PDMAPICBACKENDTYPE_HYPERV, &g_ApicNemBackend);
631 AssertLogRelRCReturn(rc, rc);
632
633 /*
634 * Allocate the map the virtual-APIC pages (for syncing the state).
635 */
636 for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
637 {
638 PVMCPU pVCpu = pVM->apCpusR3[idCpu];
639 PAPICCPU pApicCpu = VMCPU_TO_APICCPU(pVCpu);
640
641 Assert(pVCpu->idCpu == idCpu);
642 Assert(pApicCpu->pvApicPageR3 == NIL_RTR3PTR);
643 AssertCompile(sizeof(XAPICPAGE) <= HOST_PAGE_SIZE);
644 pApicCpu->cbApicPage = sizeof(XAPICPAGE);
645 rc = SUPR3PageAlloc(1 /* cHostPages */, 0 /* fFlags */, &pApicCpu->pvApicPageR3);
646 if (RT_SUCCESS(rc))
647 {
648 AssertLogRelReturn(pApicCpu->pvApicPageR3 != NIL_RTR3PTR, VERR_INTERNAL_ERROR);
649
650 /* Initialize the virtual-APIC state. */
651 RT_BZERO(pApicCpu->pvApicPageR3, pApicCpu->cbApicPage);
652 apicR3HvResetCpu(pVCpu, true /* fResetApicBaseMsr */);
653
654 PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
655 HRESULT hrc;
656 if (WHvSetVirtualProcessorState)
657 hrc = WHvSetVirtualProcessorState(pVM->nem.s.hPartition, idCpu, WHvVirtualProcessorStateTypeInterruptControllerState2,
658 pXApicPage, sizeof(*pXApicPage));
659 else
660 hrc = WHvSetVirtualProcessorInterruptControllerState2(pVM->nem.s.hPartition, idCpu, pXApicPage, sizeof(*pXApicPage));
661 AssertRelease(SUCCEEDED(hrc));
662 }
663 else
664 {
665 LogRel(("APIC%u: Failed to allocate %u bytes for the virtual-APIC page, rc=%Rrc\n", idCpu, pApicCpu->cbApicPage, rc));
666 return rc;
667 }
668 }
669
670 /*
671 * Register saved state callbacks.
672 */
673 //rc = PDMDevHlpSSMRegister(pDevIns, GIC_NEM_SAVED_STATE_VERSION, 0 /*cbGuess*/, gicR3HvSaveExec, gicR3HvLoadExec);
674 //AssertRCReturn(rc, rc);
675
676 return VINF_SUCCESS;
677}
678
679
680/**
681 * APIC device registration structure.
682 */
683const PDMDEVREG g_DeviceAPICNem =
684{
685 /* .u32Version = */ PDM_DEVREG_VERSION,
686 /* .uReserved0 = */ 0,
687 /* .szName = */ "apic-nem",
688 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE,
689 /* .fClass = */ PDM_DEVREG_CLASS_PIC,
690 /* .cMaxInstances = */ 1,
691 /* .uSharedVersion = */ 42,
692 /* .cbInstanceShared = */ sizeof(APICHVDEV),
693 /* .cbInstanceCC = */ 0,
694 /* .cbInstanceRC = */ 0,
695 /* .cMaxPciDevices = */ 0,
696 /* .cMaxMsixVectors = */ 0,
697 /* .pszDescription = */ "Advanced Programmable Interrupt Controller - Hyper-V variant",
698#if defined(IN_RING3)
699 /* .szRCMod = */ "VMMRC.rc",
700 /* .szR0Mod = */ "VMMR0.r0",
701 /* .pfnConstruct = */ apicR3HvConstruct,
702 /* .pfnDestruct = */ apicR3HvDestruct,
703 /* .pfnRelocate = */ NULL,
704 /* .pfnMemSetup = */ NULL,
705 /* .pfnPowerOn = */ NULL,
706 /* .pfnReset = */ apicR3HvReset,
707 /* .pfnSuspend = */ NULL,
708 /* .pfnResume = */ NULL,
709 /* .pfnAttach = */ NULL,
710 /* .pfnDetach = */ NULL,
711 /* .pfnQueryInterface = */ NULL,
712 /* .pfnInitComplete = */ NULL,
713 /* .pfnPowerOff = */ NULL,
714 /* .pfnSoftReset = */ NULL,
715 /* .pfnReserved0 = */ NULL,
716 /* .pfnReserved1 = */ NULL,
717 /* .pfnReserved2 = */ NULL,
718 /* .pfnReserved3 = */ NULL,
719 /* .pfnReserved4 = */ NULL,
720 /* .pfnReserved5 = */ NULL,
721 /* .pfnReserved6 = */ NULL,
722 /* .pfnReserved7 = */ NULL,
723#else
724# error "Not in IN_RING3!"
725#endif
726 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
727};
728
729/**
730 * The Hyper-V APIC backend.
731 */
732const PDMAPICBACKEND g_ApicNemBackend =
733{
734 /* .pfnIsEnabled = */ apicR3HvIsEnabled,
735 /* .pfnInitIpi = */ apicR3HvInitIpi,
736 /* .pfnGetBaseMsrNoCheck = */ apicR3HvGetBaseMsrNoCheck,
737 /* .pfnGetBaseMsr = */ apicR3HvGetBaseMsr,
738 /* .pfnSetBaseMsr = */ apicR3HvSetBaseMsr,
739 /* .pfnReadRaw32 = */ apicR3HvReadRaw32,
740 /* .pfnReadMsr = */ apicR3HvReadMsr,
741 /* .pfnWriteMsr = */ apicR3HvWriteMsr,
742 /* .pfnGetTpr = */ apicR3HvGetTpr,
743 /* .pfnSetTpr = */ apicR3HvSetTpr,
744 /* .pfnGetIcrNoCheck = */ apicR3HvGetIcrNoCheck,
745 /* .pfnSetIcr = */ apicR3HvSetIcr,
746 /* .pfnGetTimerFreq = */ apicR3HvGetTimerFreq,
747 /* .pfnSetLocalInterrupt = */ apicR3HvSetLocalInterrupt,
748 /* .pfnGetInterrupt = */ apicR3HvGetInterrupt,
749 /* .pfnPostInterrupt = */ apicR3HvPostInterrupt,
750 /* .pfnUpdatePendingInterrupts = */ apicR3HvUpdatePendingInterrupts,
751 /* .pfnBusDeliver = */ apicR3HvBusDeliver,
752 /* .pfnSetEoi = */ apicR3HvSetEoi,
753 /* .pfnHvSetCompatMode = */ apicR3NemHvSetCompatMode,
754};
755
756#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
757
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