1use 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#[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
41pub struct ResolvedPmDeps {
46 pub power_action: super::PowerActionFn,
48 pub pm_timer_assist: Option<Box<dyn PmTimerAssist>>,
50}
51
52pub 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
90pub 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
101pub 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}