Skip to main content

chipset/pm/
resolver.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Resolver for the Hyper-V power management device, and shared PM resolution
5//! logic used by both the Hyper-V and PIIX4 PM resolvers.
6
7use super::EnableAcpiMode;
8use super::PowerAction;
9use super::PowerManagementDevice;
10use async_trait::async_trait;
11use chipset_device::interrupt::LineInterruptTarget;
12use chipset_device_resources::GPE0_LINE_SET;
13use chipset_device_resources::IRQ_LINE_SET;
14use chipset_device_resources::ResolveChipsetDeviceHandleParams;
15use chipset_device_resources::ResolvedChipsetDevice;
16use chipset_resources::pm::HyperVPowerManagementDeviceHandle;
17use chipset_resources::pm::PmTimerAssist;
18use chipset_resources::pm::PmTimerAssistHandleKind;
19use power_resources::PowerRequest;
20use power_resources::PowerRequestHandleKind;
21use thiserror::Error;
22use vm_resource::AsyncResolveResource;
23use vm_resource::IntoResource;
24use vm_resource::PlatformResource;
25use vm_resource::ResolveError;
26use vm_resource::Resource;
27use vm_resource::ResourceResolver;
28use vm_resource::declare_static_async_resolver;
29use vm_resource::kind::ChipsetDeviceHandleKind;
30
31/// Errors that can occur when resolving PM device dependencies.
32#[derive(Debug, Error)]
33#[expect(missing_docs)]
34pub enum ResolvePmError {
35    #[error("failed to resolve power request")]
36    ResolvePowerRequest(#[source] ResolveError),
37    #[error("failed to resolve PM timer assist")]
38    ResolvePmTimerAssist(#[source] ResolveError),
39}
40
41/// Resolved PM dependencies (power action callback and optional timer assist).
42///
43/// Produced by [`resolve_pm_deps`] and consumed by
44/// [`PowerManagementDevice::new`].
45pub struct ResolvedPmDeps {
46    /// Callback invoked whenever a power action is requested.
47    pub power_action: super::PowerActionFn,
48    /// Optional PM timer assist, if a resource was provided.
49    pub pm_timer_assist: Option<Box<dyn PmTimerAssist>>,
50}
51
52/// Resolve the common PM dependencies (power request handler and optional PM
53/// timer assist) that are shared by both the Hyper-V and PIIX4 PM resolvers.
54///
55/// This performs the async resolution work up-front so that the resulting
56/// [`ResolvedPmDeps`] can be passed to [`PowerManagementDevice::new`]
57/// synchronously.
58pub async fn resolve_pm_deps(
59    resolver: &ResourceResolver,
60    pm_timer_assist_resource: Option<Resource<PmTimerAssistHandleKind>>,
61) -> Result<ResolvedPmDeps, ResolvePmError> {
62    let power_request = resolver
63        .resolve::<PowerRequestHandleKind, _>(PlatformResource.into_resource(), ())
64        .await
65        .map_err(ResolvePmError::ResolvePowerRequest)?;
66
67    let pm_timer_assist = if let Some(assist_resource) = pm_timer_assist_resource {
68        let resolved = resolver
69            .resolve::<PmTimerAssistHandleKind, _>(assist_resource, ())
70            .await
71            .map_err(ResolvePmError::ResolvePmTimerAssist)?;
72        Some(resolved.0)
73    } else {
74        None
75    };
76
77    Ok(ResolvedPmDeps {
78        power_action: Box::new(move |action| {
79            let req = match action {
80                PowerAction::PowerOff => PowerRequest::PowerOff,
81                PowerAction::Hibernate => PowerRequest::Hibernate,
82                PowerAction::Reboot => PowerRequest::Reset,
83            };
84            power_request.power_request(req);
85        }),
86        pm_timer_assist,
87    })
88}
89
90/// Register GPE0 line targets for a device that implements
91/// [`LineInterruptTarget`].
92pub fn register_gpe0_lines(
93    configure: &mut dyn chipset_device_resources::ConfigureChipsetDevice,
94    target: &dyn LineInterruptTarget,
95) {
96    for range in target.valid_lines() {
97        configure.add_line_target(GPE0_LINE_SET, range.clone(), *range.start());
98    }
99}
100
101/// A resolver for the Hyper-V power management device.
102pub struct HyperVPowerManagementResolver;
103
104declare_static_async_resolver! {
105    HyperVPowerManagementResolver,
106    (ChipsetDeviceHandleKind, HyperVPowerManagementDeviceHandle),
107}
108
109#[async_trait]
110impl AsyncResolveResource<ChipsetDeviceHandleKind, HyperVPowerManagementDeviceHandle>
111    for HyperVPowerManagementResolver
112{
113    type Output = ResolvedChipsetDevice;
114    type Error = ResolvePmError;
115
116    async fn resolve(
117        &self,
118        resolver: &ResourceResolver,
119        resource: HyperVPowerManagementDeviceHandle,
120        input: ResolveChipsetDeviceHandleParams<'_>,
121    ) -> Result<Self::Output, Self::Error> {
122        let acpi_interrupt = input
123            .configure
124            .new_line(IRQ_LINE_SET, "gpe0", resource.acpi_irq);
125
126        let deps = resolve_pm_deps(resolver, resource.pm_timer_assist).await?;
127
128        let pm = PowerManagementDevice::new(
129            deps.power_action,
130            acpi_interrupt,
131            input.register_pio,
132            input.vmtime.access("pm"),
133            Some(EnableAcpiMode {
134                default_pio_dynamic: resource.pio_base,
135            }),
136            deps.pm_timer_assist,
137        );
138
139        register_gpe0_lines(input.configure, &pm);
140
141        Ok(pm.into())
142    }
143}