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
25pub 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#[derive(Debug)]
45struct Acpi {
46 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 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 assert!(!apic::is_irq_assigned(facp.sci_interrupt()));
119
120 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 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
155fn 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 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}