kernel/acpi/
mod.rs

1mod aml;
2pub mod tables;
3
4use alloc::format;
5use aml::{
6    execution::{AmlExecutionError, ExecutionContext},
7    Aml,
8};
9use tables::facp;
10pub use tables::init_acpi_tables;
11use tracing::{error, info, warn};
12
13use crate::{
14    cpu::{
15        self,
16        idt::{BasicInterruptHandler, InterruptStackFrame64},
17        interrupts::apic,
18    },
19    power,
20    sync::once::OnceLock,
21};
22
23static ACPI: OnceLock<Acpi> = OnceLock::new();
24
25/// Setup interrupts and request ownership of ACPI
26pub fn init() {
27    ACPI.set(Acpi::init())
28        .expect("ACPI was already initialized");
29
30    info!("ACPI initialized");
31}
32
33pub fn sleep(ty: u8) -> Result<(), AcpiError> {
34    ACPI.get().sleep(ty)
35}
36
37#[derive(Debug)]
38pub enum AcpiError {
39    InvalidSleepType,
40    SleepTypeNotAvailable,
41}
42
43/// Stores some items and data related to ACPI
44#[derive(Debug)]
45struct Acpi {
46    /// SLP_TYPa and SLP_TYPb data for \_S1_ until \_S5_
47    slp_type_data: [Option<[u8; 2]>; 5],
48}
49
50impl Acpi {
51    fn init() -> Acpi {
52        Self::enable();
53
54        let mut slp_type_data = [None, None, None, None, None];
55
56        for table in tables::get_acpi_tables().rsdt.iter_tables::<tables::Xsdt>() {
57            for (i, slp_data) in slp_type_data.iter_mut().enumerate() {
58                if slp_data.is_some() {
59                    continue;
60                }
61
62                if let Some(result) = fetch_s_array(&table.aml, &format!("\\_S{}_", i + 1)) {
63                    *slp_data = Some(result);
64                }
65            }
66        }
67
68        info!("SLP_TYPa and SLP_TYPb data: {:?}", slp_type_data);
69
70        Acpi { slp_type_data }
71    }
72
73    fn sleep(&self, ty: u8) -> Result<(), AcpiError> {
74        if ty == 0 || ty > 5 {
75            return Err(AcpiError::InvalidSleepType);
76        }
77
78        if let Some(slp_data) = &self.slp_type_data[ty as usize - 1] {
79            let facp = tables::get_acpi_tables()
80                .rsdt
81                .get_table::<tables::Facp>()
82                .expect("No Facp");
83
84            let mut ctrl_a = facp.read_pm1_control_a();
85            let ctrl_b = facp.read_pm1_control_b();
86
87            ctrl_a = (ctrl_a & !facp::flags::PM_CTRL_SLP_TYP_MASK)
88                | ((slp_data[0] as u16) << facp::flags::PM_CTRL_SLP_TYP_SHIFT);
89
90            info!("Entering Sleep mode {ty}!");
91
92            if let Some(mut ctrl_b) = ctrl_b {
93                ctrl_b = (ctrl_b & !facp::flags::PM_CTRL_SLP_TYP_MASK)
94                    | ((slp_data[1] as u16) << facp::flags::PM_CTRL_SLP_TYP_SHIFT);
95                facp.write_pm1_control_b(ctrl_b);
96            }
97
98            facp.write_pm1_control_a(ctrl_a | facp::flags::PM_CTRL_SLP_EN);
99
100            // poll WAK_STS until woken
101            while facp.read_pm1_status() & facp::flags::PM_STS_WAK == 0 {
102                core::hint::spin_loop();
103            }
104
105            Ok(())
106        } else {
107            Err(AcpiError::SleepTypeNotAvailable)
108        }
109    }
110
111    fn enable() {
112        let facp = tables::get_acpi_tables()
113            .rsdt
114            .get_table::<tables::Facp>()
115            .expect("No Facp");
116
117        // make sure we haven't already enabled ACPI
118        assert!(!apic::is_irq_assigned(facp.sci_interrupt()));
119
120        // disable the events first
121        facp.write_pm1_enable(0);
122
123        apic::assign_io_irq(
124            acpi_handler as BasicInterruptHandler,
125            facp.sci_interrupt(),
126            cpu::cpu(),
127        );
128
129        if !facp.is_acpi_enabled() {
130            facp.enable_acpi();
131        } else {
132            warn!("ACPI already enabled");
133        }
134
135        let mut i = 0;
136        while !facp.is_acpi_enabled() && i < 10000 {
137            i += 1;
138            core::hint::spin_loop();
139        }
140
141        if !facp.is_acpi_enabled() {
142            panic!("Failed to enable ACPI");
143        }
144
145        // enable all events except timer
146        facp.write_pm1_enable(
147            facp::flags::PM_EN_GBL
148                | facp::flags::PM_EN_PWRBTN
149                | facp::flags::PM_EN_SLPBTN
150                | facp::flags::PM_EN_RTC,
151        );
152    }
153}
154
155/// Halper function
156fn fetch_s_array(aml: &Aml, name: &str) -> Option<[u8; 2]> {
157    let mut ctx = ExecutionContext::default();
158    match aml.execute(&mut ctx, name, &[]) {
159        Ok(obj) => {
160            let Some(package) = obj.as_package() else {
161                error!("{} is not a package", name);
162                return None;
163            };
164
165            if package.size() < 2 {
166                error!("{} package is ony {} in size", name, package.size());
167            }
168
169            let mut slp_data = [0; 2];
170
171            for (i, item) in package
172                .iter()
173                .take(2)
174                .map(|obj| {
175                    let Some(data) = obj.as_data() else {
176                        error!("{:?} is not a data", obj);
177                        return None;
178                    };
179
180                    let Some(int) = data.as_integer() else {
181                        error!("{:?} is not an integer", data);
182                        return None;
183                    };
184
185                    if let Some(mut int) = int.as_u8() {
186                        if int & !7 != 0 {
187                            warn!(
188                                "Data {:02X} of {name:?} is more than 3 bits, truncating...",
189                                int
190                            );
191                            int &= 7;
192                        }
193                        Some(int)
194                    } else {
195                        error!("{:?} is not a u8", int);
196                        None
197                    }
198                })
199                .enumerate()
200            {
201                if let Some(item) = item {
202                    slp_data[i] = item;
203                } else {
204                    return None;
205                }
206            }
207
208            Some(slp_data)
209        }
210        Err(AmlExecutionError::LableNotFound(_)) => None,
211        Err(e) => {
212            error!("Failed to execute AML for {name}: {:?}", e);
213            None
214        }
215    }
216}
217
218extern "x86-interrupt" fn acpi_handler(_frame: InterruptStackFrame64) {
219    let facp = tables::get_acpi_tables()
220        .rsdt
221        .get_table::<tables::Facp>()
222        .expect("No Facp");
223
224    let pm1_event = facp.read_pm1_status();
225
226    if pm1_event & facp::flags::PM_EN_GBL != 0 {
227        facp.write_pm1_status(facp::flags::PM_EN_GBL);
228        warn!("Global ACPI event: {:X}", pm1_event);
229    } else if pm1_event & facp::flags::PM_EN_SLPBTN != 0 {
230        facp.write_pm1_status(facp::flags::PM_EN_SLPBTN);
231        warn!("Sleep button ACPI event: {:X}", pm1_event);
232    } else if pm1_event & facp::flags::PM_EN_RTC != 0 {
233        facp.write_pm1_status(facp::flags::PM_EN_RTC);
234        warn!("RTC ACPI event: {:X}", pm1_event);
235    } else if pm1_event & facp::flags::PM_EN_PWRBTN != 0 {
236        facp.write_pm1_status(facp::flags::PM_EN_PWRBTN);
237        warn!("Power button ACPI event: {:X}", pm1_event);
238
239        // TODO: handle shutdown setup
240        power::start_power_sequence(power::PowerCommand::Shutdown);
241    } else if pm1_event & facp::flags::PM_EN_TMR != 0 {
242        facp.write_pm1_status(facp::flags::PM_EN_TMR);
243        warn!("Timer ACPI event: {:X}", pm1_event);
244    } else {
245        warn!("Unknown ACPI event: {:X}", pm1_event);
246    }
247
248    apic::return_from_interrupt();
249}