Serenity Operating System
1/*
2 * Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
3 * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright notice, this
10 * list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include <AK/StringView.h>
29#include <Kernel/ACPI/Parser.h>
30#include <Kernel/PCI/Access.h>
31#include <Kernel/VM/MemoryManager.h>
32#include <Kernel/VM/TypedMapping.h>
33#include <LibBareMetal/IO.h>
34#include <LibBareMetal/StdLib.h>
35
36namespace Kernel {
37namespace ACPI {
38
39static Parser* s_acpi_parser;
40
41Parser* Parser::the()
42{
43 return s_acpi_parser;
44}
45
46void Parser::set_the(Parser& parser)
47{
48 ASSERT(!s_acpi_parser);
49 s_acpi_parser = &parser;
50}
51
52static bool match_table_signature(PhysicalAddress table_header, const StringView& signature);
53static PhysicalAddress search_table_in_xsdt(PhysicalAddress xsdt, const StringView& signature);
54static PhysicalAddress search_table_in_rsdt(PhysicalAddress rsdt, const StringView& signature);
55static bool validate_table(const Structures::SDTHeader&, size_t length);
56
57void Parser::locate_static_data()
58{
59 locate_main_system_description_table();
60 initialize_main_system_description_table();
61 init_fadt();
62 init_facs();
63}
64
65PhysicalAddress Parser::find_table(const StringView& signature)
66{
67#ifdef ACPI_DEBUG
68 dbg() << "ACPI: Calling Find Table method!";
69#endif
70 for (auto p_sdt : m_sdt_pointers) {
71 auto sdt = map_typed<Structures::SDTHeader>(p_sdt);
72#ifdef ACPI_DEBUG
73 dbg() << "ACPI: Examining Table @ P " << p_sdt;
74#endif
75 if (!strncmp(sdt->sig, signature.characters_without_null_termination(), 4)) {
76#ifdef ACPI_DEBUG
77 dbg() << "ACPI: Found Table @ P " << p_sdt;
78#endif
79 return p_sdt;
80 }
81 }
82 return {};
83}
84
85void Parser::init_facs()
86{
87 m_facs = find_table("FACS");
88}
89
90void Parser::init_fadt()
91{
92 klog() << "ACPI: Initializing Fixed ACPI data";
93 klog() << "ACPI: Searching for the Fixed ACPI Data Table";
94
95 m_fadt = find_table("FACP");
96 ASSERT(!m_fadt.is_null());
97
98 auto sdt = map_typed<Structures::FADT>(m_fadt);
99
100#ifdef ACPI_DEBUG
101 dbg() << "ACPI: FADT @ V " << sdt << ", P " << (void*)m_fadt.as_ptr();
102#endif
103 klog() << "ACPI: Fixed ACPI data, Revision " << sdt->h.revision << ", Length " << sdt->h.length << " bytes";
104 klog() << "ACPI: DSDT " << PhysicalAddress(sdt->dsdt_ptr);
105 m_x86_specific_flags.cmos_rtc_not_present = (sdt->ia_pc_boot_arch_flags & (u8)FADTFlags::IA_PC_Flags::CMOS_RTC_Not_Present);
106 m_x86_specific_flags.keyboard_8042 = (sdt->ia_pc_boot_arch_flags & (u8)FADTFlags::IA_PC_Flags::PS2_8042);
107 m_x86_specific_flags.legacy_devices = (sdt->ia_pc_boot_arch_flags & (u8)FADTFlags::IA_PC_Flags::Legacy_Devices);
108 m_x86_specific_flags.msi_not_supported = (sdt->ia_pc_boot_arch_flags & (u8)FADTFlags::IA_PC_Flags::MSI_Not_Supported);
109 m_x86_specific_flags.vga_not_present = (sdt->ia_pc_boot_arch_flags & (u8)FADTFlags::IA_PC_Flags::VGA_Not_Present);
110
111 m_hardware_flags.cpu_software_sleep = (sdt->flags & (u32)FADTFlags::FeatureFlags::CPU_SW_SLP);
112 m_hardware_flags.docking_capability = (sdt->flags & (u32)FADTFlags::FeatureFlags::DCK_CAP);
113 m_hardware_flags.fix_rtc = (sdt->flags & (u32)FADTFlags::FeatureFlags::FIX_RTC);
114 m_hardware_flags.force_apic_cluster_model = (sdt->flags & (u32)FADTFlags::FeatureFlags::FORCE_APIC_CLUSTER_MODEL);
115 m_hardware_flags.force_apic_physical_destination_mode = (sdt->flags & (u32)FADTFlags::FeatureFlags::FORCE_APIC_PHYSICAL_DESTINATION_MODE);
116 m_hardware_flags.hardware_reduced_acpi = (sdt->flags & (u32)FADTFlags::FeatureFlags::HW_REDUCED_ACPI);
117 m_hardware_flags.headless = (sdt->flags & (u32)FADTFlags::FeatureFlags::HEADLESS);
118 m_hardware_flags.low_power_s0_idle_capable = (sdt->flags & (u32)FADTFlags::FeatureFlags::LOW_POWER_S0_IDLE_CAPABLE);
119 m_hardware_flags.multiprocessor_c2 = (sdt->flags & (u32)FADTFlags::FeatureFlags::P_LVL2_UP);
120 m_hardware_flags.pci_express_wake = (sdt->flags & (u32)FADTFlags::FeatureFlags::PCI_EXP_WAK);
121 m_hardware_flags.power_button = (sdt->flags & (u32)FADTFlags::FeatureFlags::PWR_BUTTON);
122 m_hardware_flags.processor_c1 = (sdt->flags & (u32)FADTFlags::FeatureFlags::PROC_C1);
123 m_hardware_flags.remote_power_on_capable = (sdt->flags & (u32)FADTFlags::FeatureFlags::REMOTE_POWER_ON_CAPABLE);
124 m_hardware_flags.reset_register_supported = (sdt->flags & (u32)FADTFlags::FeatureFlags::RESET_REG_SUPPORTED);
125 m_hardware_flags.rtc_s4 = (sdt->flags & (u32)FADTFlags::FeatureFlags::RTC_s4);
126 m_hardware_flags.s4_rtc_status_valid = (sdt->flags & (u32)FADTFlags::FeatureFlags::S4_RTC_STS_VALID);
127 m_hardware_flags.sealed_case = (sdt->flags & (u32)FADTFlags::FeatureFlags::SEALED_CASE);
128 m_hardware_flags.sleep_button = (sdt->flags & (u32)FADTFlags::FeatureFlags::SLP_BUTTON);
129 m_hardware_flags.timer_value_extension = (sdt->flags & (u32)FADTFlags::FeatureFlags::TMR_VAL_EXT);
130 m_hardware_flags.use_platform_clock = (sdt->flags & (u32)FADTFlags::FeatureFlags::USE_PLATFORM_CLOCK);
131 m_hardware_flags.wbinvd = (sdt->flags & (u32)FADTFlags::FeatureFlags::WBINVD);
132 m_hardware_flags.wbinvd_flush = (sdt->flags & (u32)FADTFlags::FeatureFlags::WBINVD_FLUSH);
133}
134
135bool Parser::can_reboot()
136{
137 auto fadt = map_typed<Structures::FADT>(m_fadt);
138 if (fadt->h.revision < 2)
139 return false;
140 return m_hardware_flags.reset_register_supported;
141}
142
143void Parser::access_generic_address(const Structures::GenericAddressStructure& structure, u32 value)
144{
145 switch ((GenericAddressStructure::AddressSpace)structure.address_space) {
146 case GenericAddressStructure::AddressSpace::SystemIO: {
147 IOAddress address(structure.address);
148 dbg() << "ACPI: Sending value 0x" << String::format("%x", value) << " to " << address;
149 switch (structure.access_size) {
150 case (u8)GenericAddressStructure::AccessSize::QWord: {
151 dbg() << "Trying to send QWord to IO port";
152 ASSERT_NOT_REACHED();
153 break;
154 }
155 case (u8)GenericAddressStructure::AccessSize::Undefined: {
156 dbg() << "ACPI Warning: Unknown access size " << structure.access_size;
157 ASSERT(structure.bit_width != (u8)GenericAddressStructure::BitWidth::QWord);
158 ASSERT(structure.bit_width != (u8)GenericAddressStructure::BitWidth::Undefined);
159 dbg() << "ACPI: Bit Width - " << structure.bit_width << " bits";
160 address.out(value, structure.bit_width);
161 break;
162 }
163 default:
164 address.out(value, (8 << (structure.access_size - 1)));
165 break;
166 }
167 return;
168 }
169 case GenericAddressStructure::AddressSpace::SystemMemory: {
170 dbg() << "ACPI: Sending value 0x" << String::format("%x", value) << " to " << PhysicalAddress(structure.address);
171 switch ((GenericAddressStructure::AccessSize)structure.access_size) {
172 case GenericAddressStructure::AccessSize::Byte:
173 *map_typed<u8>(PhysicalAddress(structure.address)) = value;
174 break;
175 case GenericAddressStructure::AccessSize::Word:
176 *map_typed<u16>(PhysicalAddress(structure.address)) = value;
177 break;
178 case GenericAddressStructure::AccessSize::DWord:
179 *map_typed<u32>(PhysicalAddress(structure.address)) = value;
180 break;
181 case GenericAddressStructure::AccessSize::QWord: {
182 *map_typed<u64>(PhysicalAddress(structure.address)) = value;
183 break;
184 }
185 default:
186 ASSERT_NOT_REACHED();
187 }
188 return;
189 }
190 case GenericAddressStructure::AddressSpace::PCIConfigurationSpace: {
191 // According to the ACPI specification 6.2, page 168, PCI addresses must be confined to devices on Segment group 0, bus 0.
192 auto pci_address = PCI::Address(0, 0, ((structure.address >> 24) & 0xFF), ((structure.address >> 16) & 0xFF));
193 dbg() << "ACPI: Sending value 0x" << String::format("%x", value) << " to " << pci_address;
194 u32 offset_in_pci_address = structure.address & 0xFFFF;
195 if (structure.access_size == (u8)GenericAddressStructure::AccessSize::QWord) {
196 dbg() << "Trying to send QWord to PCI configuration space";
197 ASSERT_NOT_REACHED();
198 }
199 ASSERT(structure.access_size != (u8)GenericAddressStructure::AccessSize::Undefined);
200 PCI::raw_access(pci_address, offset_in_pci_address, (1 << (structure.access_size - 1)), value);
201 return;
202 }
203 default:
204 ASSERT_NOT_REACHED();
205 }
206 ASSERT_NOT_REACHED();
207}
208
209bool Parser::validate_reset_register()
210{
211 // According to the ACPI spec 6.2, page 152, The reset register can only be located in I/O bus, PCI bus or memory-mapped.
212 auto fadt = map_typed<Structures::FADT>(m_fadt);
213 return (fadt->reset_reg.address_space == (u8)GenericAddressStructure::AddressSpace::PCIConfigurationSpace || fadt->reset_reg.address_space == (u8)GenericAddressStructure::AddressSpace::SystemMemory || fadt->reset_reg.address_space == (u8)GenericAddressStructure::AddressSpace::SystemIO);
214}
215
216void Parser::try_acpi_reboot()
217{
218 InterruptDisabler disabler;
219 if (!can_reboot()) {
220 klog() << "ACPI: Reboot, Not supported!";
221 return;
222 }
223#ifdef ACPI_DEBUG
224 dbg() << "ACPI: Rebooting, Probing FADT (" << m_fadt << ")";
225#endif
226
227 auto fadt = map_typed<Structures::FADT>(m_fadt);
228 ASSERT(validate_reset_register());
229 access_generic_address(fadt->reset_reg, fadt->reset_value);
230 hang();
231}
232
233void Parser::try_acpi_shutdown()
234{
235 klog() << "ACPI: Shutdown is not supported with the current configuration, Abort!";
236}
237
238size_t Parser::get_table_size(PhysicalAddress table_header)
239{
240 InterruptDisabler disabler;
241#ifdef ACPI_DEBUG
242 dbg() << "ACPI: Checking SDT Length";
243#endif
244 return map_typed<Structures::SDTHeader>(table_header)->length;
245}
246
247u8 Parser::get_table_revision(PhysicalAddress table_header)
248{
249 InterruptDisabler disabler;
250#ifdef ACPI_DEBUG
251 dbg() << "ACPI: Checking SDT Revision";
252#endif
253 return map_typed<Structures::SDTHeader>(table_header)->revision;
254}
255
256void Parser::initialize_main_system_description_table()
257{
258#ifdef ACPI_DEBUG
259 dbg() << "ACPI: Checking Main SDT Length to choose the correct mapping size";
260#endif
261 ASSERT(!m_main_system_description_table.is_null());
262 auto length = get_table_size(m_main_system_description_table);
263 auto revision = get_table_revision(m_main_system_description_table);
264
265 auto sdt = map_typed<Structures::SDTHeader>(m_main_system_description_table, length);
266
267 klog() << "ACPI: Main Description Table valid? " << validate_table(*sdt, length);
268
269 if (m_xsdt_supported) {
270 auto& xsdt = (const Structures::XSDT&)*sdt;
271 klog() << "ACPI: Using XSDT, Enumerating tables @ " << m_main_system_description_table;
272 klog() << "ACPI: XSDT Revision " << revision << ", Total length - " << length;
273#ifdef ACPI_DEBUG
274 dbg() << "ACPI: XSDT pointer @ V " << xsdt;
275#endif
276 for (u32 i = 0; i < ((length - sizeof(Structures::SDTHeader)) / sizeof(u64)); i++) {
277#ifdef ACPI_DEBUG
278 dbg() << "ACPI: Found new table [" << i << "], @ V 0x" << String::format("%x", &xsdt.table_ptrs[i]) << " - P 0x" << String::format("%x", xsdt.table_ptrs[i]);
279#endif
280 m_sdt_pointers.append(PhysicalAddress(xsdt.table_ptrs[i]));
281 }
282 } else {
283 auto& rsdt = (const Structures::RSDT&)*sdt;
284 klog() << "ACPI: Using RSDT, Enumerating tables @ " << m_main_system_description_table;
285 klog() << "ACPI: RSDT Revision " << revision << ", Total length - " << length;
286#ifdef ACPI_DEBUG
287 dbg() << "ACPI: RSDT pointer @ V " << rsdt;
288#endif
289 for (u32 i = 0; i < ((length - sizeof(Structures::SDTHeader)) / sizeof(u32)); i++) {
290#ifdef ACPI_DEBUG
291 dbg() << "ACPI: Found new table [" << i << "], @ V 0x" << String::format("%x", &rsdt.table_ptrs[i]) << " - P 0x" << String::format("%x", rsdt.table_ptrs[i]);
292#endif
293 m_sdt_pointers.append(PhysicalAddress(rsdt.table_ptrs[i]));
294 }
295 }
296}
297
298void Parser::locate_main_system_description_table()
299{
300 auto rsdp = map_typed<Structures::RSDPDescriptor20>(m_rsdp);
301 if (rsdp->base.revision == 0) {
302 m_xsdt_supported = false;
303 } else if (rsdp->base.revision >= 2) {
304 if (rsdp->xsdt_ptr != (u64) nullptr) {
305 m_xsdt_supported = true;
306 } else {
307 m_xsdt_supported = false;
308 }
309 }
310 if (!m_xsdt_supported) {
311 m_main_system_description_table = PhysicalAddress(rsdp->base.rsdt_ptr);
312 } else {
313 m_main_system_description_table = PhysicalAddress(rsdp->xsdt_ptr);
314 }
315}
316
317Parser::Parser(PhysicalAddress rsdp)
318 : m_rsdp(rsdp)
319{
320 klog() << "ACPI: Using RSDP @ " << rsdp;
321 locate_static_data();
322}
323
324static PhysicalAddress find_rsdp_in_ebda(u16 ebda_segment)
325{
326 auto rsdp_region = MM.allocate_kernel_region(PhysicalAddress(page_base_of((u32)(ebda_segment << 4))), PAGE_ROUND_UP(1024), "ACPI Static Parser RSDP Finding #1", Region::Access::Read, false, true);
327 char* p_rsdp_str = (char*)(PhysicalAddress(ebda_segment << 4).as_ptr());
328 for (char* rsdp_str = (char*)rsdp_region->vaddr().offset(offset_in_page((u32)(ebda_segment << 4))).as_ptr(); rsdp_str < (char*)(rsdp_region->vaddr().offset(offset_in_page((u32)(ebda_segment << 4))).get() + 1024); rsdp_str += 16) {
329#ifdef ACPI_DEBUG
330 dbg() << "ACPI: Looking for RSDP in EBDA @ V " << (void*)rsdp_str << ", P " << (void*)p_rsdp_str;
331#endif
332 if (!strncmp("RSD PTR ", rsdp_str, strlen("RSD PTR ")))
333 return PhysicalAddress((FlatPtr)p_rsdp_str);
334 p_rsdp_str += 16;
335 }
336 return {};
337}
338
339static PhysicalAddress find_rsdp_in_bios_area()
340{
341 auto rsdp_region = MM.allocate_kernel_region(PhysicalAddress(0xE0000), PAGE_ROUND_UP(0xFFFFF - 0xE0000), "ACPI Static Parser RSDP Finding #2", Region::Access::Read, false, true);
342 char* p_rsdp_str = (char*)(PhysicalAddress(0xE0000).as_ptr());
343 for (char* rsdp_str = (char*)rsdp_region->vaddr().offset(offset_in_page((u32)(0xE0000))).as_ptr(); rsdp_str < (char*)(rsdp_region->vaddr().offset(offset_in_page((u32)(0xE0000))).get() + (0xFFFFF - 0xE0000)); rsdp_str += 16) {
344#ifdef ACPI_DEBUG
345 dbg() << "ACPI: Looking for RSDP in BIOS ROM area @ V " << (void*)rsdp_str << ", P " << (void*)p_rsdp_str;
346#endif
347 if (!strncmp("RSD PTR ", rsdp_str, strlen("RSD PTR ")))
348 return PhysicalAddress((FlatPtr)p_rsdp_str);
349 p_rsdp_str += 16;
350 }
351 return {};
352}
353
354static bool validate_table(const Structures::SDTHeader& v_header, size_t length)
355{
356 u8 checksum = 0;
357 auto* sdt = (const u8*)&v_header;
358 for (size_t i = 0; i < length; i++)
359 checksum += sdt[i];
360 if (checksum == 0)
361 return true;
362 return false;
363}
364
365PhysicalAddress StaticParsing::find_rsdp()
366{
367 auto ebda_seg_ptr = map_typed<u16>(PhysicalAddress(0x40e));
368 klog() << "ACPI: Probing EBDA, Segment 0x" << String::format("%x", *ebda_seg_ptr);
369 auto rsdp = find_rsdp_in_ebda(*ebda_seg_ptr);
370 if (!rsdp.is_null())
371 return rsdp;
372 return find_rsdp_in_bios_area();
373}
374
375PhysicalAddress StaticParsing::find_table(PhysicalAddress rsdp_address, const StringView& signature)
376{
377 // FIXME: There's no validation of ACPI tables here. Use the checksum to validate the tables.
378 // FIXME: Don't blindly use PAGE_SIZE here, but probe the actual length.
379 ASSERT(signature.length() == 4);
380
381 auto rsdp = map_typed<Structures::RSDPDescriptor20>(rsdp_address);
382
383 if (rsdp->base.revision == 0)
384 return search_table_in_rsdt(PhysicalAddress(rsdp->base.rsdt_ptr), signature);
385
386 if (rsdp->base.revision >= 2) {
387 if (rsdp->xsdt_ptr)
388 return search_table_in_xsdt(PhysicalAddress(rsdp->xsdt_ptr), signature);
389 return search_table_in_rsdt(PhysicalAddress(rsdp->base.rsdt_ptr), signature);
390 }
391 ASSERT_NOT_REACHED();
392}
393
394static PhysicalAddress search_table_in_xsdt(PhysicalAddress xsdt_address, const StringView& signature)
395{
396 // FIXME: There's no validation of ACPI tables here. Use the checksum to validate the tables.
397 // FIXME: Don't blindly use PAGE_SIZE here, but probe the actual length.
398 ASSERT(signature.length() == 4);
399
400 auto xsdt = map_typed<Structures::XSDT>(xsdt_address);
401
402 for (size_t i = 0; i < ((xsdt->h.length - sizeof(Structures::SDTHeader)) / sizeof(u64)); ++i) {
403 if (match_table_signature(PhysicalAddress((FlatPtr)xsdt->table_ptrs[i]), signature))
404 return PhysicalAddress((FlatPtr)xsdt->table_ptrs[i]);
405 }
406 return {};
407}
408
409static bool match_table_signature(PhysicalAddress table_header, const StringView& signature)
410{
411 // FIXME: There's no validation of ACPI tables here. Use the checksum to validate the tables.
412 // FIXME: Don't blindly use PAGE_SIZE here, but probe the actual length.
413 ASSERT(signature.length() == 4);
414
415 auto table = map_typed<Structures::RSDT>(table_header);
416 return !strncmp(table->h.sig, signature.characters_without_null_termination(), 4);
417}
418
419static PhysicalAddress search_table_in_rsdt(PhysicalAddress rsdt_address, const StringView& signature)
420{
421 // FIXME: There's no validation of ACPI tables here. Use the checksum to validate the tables.
422 // FIXME: Don't blindly use PAGE_SIZE here, but probe the actual length.
423 ASSERT(signature.length() == 4);
424
425 auto rsdt = map_typed<Structures::RSDT>(rsdt_address);
426
427 for (u32 i = 0; i < ((rsdt->h.length - sizeof(Structures::SDTHeader)) / sizeof(u32)); i++) {
428 if (match_table_signature(PhysicalAddress((FlatPtr)rsdt->table_ptrs[i]), signature))
429 return PhysicalAddress((FlatPtr)rsdt->table_ptrs[i]);
430 }
431 return {};
432}
433
434void Parser::enable_aml_interpretation()
435{
436 ASSERT_NOT_REACHED();
437}
438
439void Parser::enable_aml_interpretation(File&)
440{
441 ASSERT_NOT_REACHED();
442}
443
444void Parser::enable_aml_interpretation(u8*, u32)
445{
446 ASSERT_NOT_REACHED();
447}
448
449void Parser::disable_aml_interpretation()
450{
451 ASSERT_NOT_REACHED();
452}
453
454}
455}