vmm/device_manager/
acpi.rs

1// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use acpi_tables::{Aml, aml};
5use vm_memory::GuestMemoryError;
6
7use crate::Vm;
8#[cfg(target_arch = "x86_64")]
9use crate::devices::acpi::vmclock::VmClock;
10use crate::devices::acpi::vmgenid::VmGenId;
11use crate::vstate::resources::ResourceAllocator;
12
13#[derive(Debug, thiserror::Error, displaydoc::Display)]
14pub enum ACPIDeviceError {
15    /// Could not register GSI with KVM: {0}
16    RegisterIrq(#[from] kvm_ioctls::Error),
17    /// Could not write to guest memory: {0}
18    WriteGuestMemory(#[from] GuestMemoryError),
19}
20
21#[derive(Debug)]
22pub struct ACPIDeviceManager {
23    /// VMGenID device
24    pub vmgenid: VmGenId,
25    /// VMclock device
26    #[cfg(target_arch = "x86_64")]
27    pub vmclock: VmClock,
28}
29
30impl ACPIDeviceManager {
31    /// Create a new ACPIDeviceManager object
32    pub fn new(resource_allocator: &mut ResourceAllocator) -> Self {
33        ACPIDeviceManager {
34            vmgenid: VmGenId::new(resource_allocator),
35            #[cfg(target_arch = "x86_64")]
36            vmclock: VmClock::new(resource_allocator),
37        }
38    }
39
40    pub fn attach_vmgenid(&self, vm: &Vm) -> Result<(), ACPIDeviceError> {
41        vm.register_irq(&self.vmgenid.interrupt_evt, self.vmgenid.gsi)?;
42        self.vmgenid.activate(vm.guest_memory())?;
43        Ok(())
44    }
45
46    #[cfg(target_arch = "x86_64")]
47    pub fn attach_vmclock(&self, vm: &Vm) -> Result<(), ACPIDeviceError> {
48        self.vmclock.activate(vm.guest_memory())?;
49        Ok(())
50    }
51}
52
53impl Aml for ACPIDeviceManager {
54    fn append_aml_bytes(&self, v: &mut Vec<u8>) -> Result<(), aml::AmlError> {
55        // AML for [`VmGenId`] device.
56        self.vmgenid.append_aml_bytes(v)?;
57        // AML for [`VmClock`] device.
58        #[cfg(target_arch = "x86_64")]
59        self.vmclock.append_aml_bytes(v)?;
60
61        // Create the AML for the GED interrupt handler
62        aml::Device::new(
63            "_SB_.GED_".try_into()?,
64            vec![
65                &aml::Name::new("_HID".try_into()?, &"ACPI0013")?,
66                &aml::Name::new(
67                    "_CRS".try_into()?,
68                    &aml::ResourceTemplate::new(vec![&aml::Interrupt::new(
69                        true,
70                        true,
71                        false,
72                        false,
73                        self.vmgenid.gsi,
74                    )]),
75                )?,
76                &aml::Method::new(
77                    "_EVT".try_into()?,
78                    1,
79                    true,
80                    vec![&aml::If::new(
81                        // We know that the maximum IRQ number fits in a u8. We have up to
82                        // 32 IRQs in x86 and up to 128 in
83                        // ARM (look into
84                        // `vmm::crate::arch::layout::GSI_LEGACY_END`)
85                        #[allow(clippy::cast_possible_truncation)]
86                        &aml::Equal::new(&aml::Arg(0), &(self.vmgenid.gsi as u8)),
87                        vec![&aml::Notify::new(
88                            &aml::Path::new("\\_SB_.VGEN")?,
89                            &0x80usize,
90                        )],
91                    )],
92                ),
93            ],
94        )
95        .append_aml_bytes(v)
96    }
97}