Serenity Operating System
at portability 408 lines 16 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <Kernel/ACPI/ACPIStaticParser.h> 28#include <Kernel/VM/MemoryManager.h> 29#include <LibBareMetal/IO.h> 30#include <LibBareMetal/StdLib.h> 31 32//#define ACPI_DEBUG 33 34namespace Kernel { 35 36void ACPIStaticParser::initialize(PhysicalAddress rsdp) 37{ 38 if (!ACPIParser::is_initialized()) { 39 new ACPIStaticParser(rsdp); 40 } 41} 42void ACPIStaticParser::initialize_without_rsdp() 43{ 44 if (!ACPIParser::is_initialized()) { 45 new ACPIStaticParser(); 46 } 47} 48 49bool ACPIStaticParser::is_initialized() 50{ 51 return ACPIParser::is_initialized(); 52} 53 54void ACPIStaticParser::locate_static_data() 55{ 56 locate_main_system_description_table(); 57 initialize_main_system_description_table(); 58 init_fadt(); 59 locate_all_aml_tables(); 60} 61 62PhysicalAddress ACPIStaticParser::find_table(const char* sig) 63{ 64#ifdef ACPI_DEBUG 65 dbgprintf("ACPI: Calling Find Table method!\n"); 66#endif 67 for (auto* physical_sdt_ptr : m_main_sdt->get_sdt_pointers()) { 68 auto region = MM.allocate_kernel_region(PhysicalAddress(page_base_of(physical_sdt_ptr)), (PAGE_SIZE * 2), "ACPI Static Parser Tables Finding", Region::Access::Read); 69 auto* sdt = (const ACPI_RAW::SDTHeader*)region->vaddr().offset(offset_in_page(physical_sdt_ptr)).as_ptr(); 70#ifdef ACPI_DEBUG 71 dbgprintf("ACPI: Examining Table @ P 0x%x\n", physical_sdt_ptr); 72#endif 73 if (!strncmp(sdt->sig, sig, 4)) { 74#ifdef ACPI_DEBUG 75 dbgprintf("ACPI: Found Table @ P 0x%x\n", physical_sdt_ptr); 76#endif 77 return PhysicalAddress((uintptr_t)physical_sdt_ptr); 78 } 79 } 80 return {}; 81} 82 83void ACPIStaticParser::init_fadt() 84{ 85 kprintf("ACPI: Initializing Fixed ACPI data\n"); 86 kprintf("ACPI: Searching for the Fixed ACPI Data Table\n"); 87 88 auto fadt = find_table("FACP"); 89 ASSERT(!fadt.is_null()); 90 91 auto checkup_region = MM.allocate_kernel_region(fadt.page_base(), (PAGE_SIZE * 2), "ACPI Static Parser", Region::Access::Read); 92#ifdef ACPI_DEBUG 93 dbgprintf("ACPI: Checking FADT Length to choose the correct mapping size\n"); 94#endif 95 96 auto* sdt = (const ACPI_RAW::SDTHeader*)checkup_region->vaddr().offset(fadt.offset_in_page().get()).as_ptr(); 97#ifdef ACPI_DEBUG 98 dbgprintf("ACPI: FADT @ V 0x%x, P 0x%x\n", sdt, fadt_ptr); 99#endif 100 u32 length = sdt->length; 101 kprintf("ACPI: Fixed ACPI data, Revision %u\n", sdt->revision); 102 103 auto fadt_region = MM.allocate_kernel_region(fadt.page_base(), PAGE_ROUND_UP(length) + PAGE_SIZE, "ACPI Static Parser", Region::Access::Read); 104 m_fadt = make<ACPI::FixedACPIData>(*(ACPI_RAW::FADT*)fadt_region->vaddr().offset(fadt.offset_in_page().get()).as_ptr()); 105#ifdef ACPI_DEBUG 106 dbgprintf("ACPI: Finished to initialize Fixed ACPI data\n"); 107#endif 108} 109 110void ACPIStaticParser::do_acpi_reboot() 111{ 112 // FIXME: Determine if we need to do MMIO/PCI/IO access to reboot, according to ACPI spec 6.2, Section 4.8.3.6 113#ifdef ACPI_DEBUG 114 dbgprintf("ACPI: Rebooting, Probing FADT (P @ 0x%x)\n", m_fadt.ptr()); 115#endif 116 if (m_fadt->m_revision >= 2) { 117 kprintf("ACPI: Reboot, Sending value 0%x to Port 0x%x\n", m_fadt->m_reset_value, m_fadt->m_reset_reg.address); 118 IO::out8(m_fadt->m_reset_reg.address, m_fadt->m_reset_value); 119 } else { 120 kprintf("ACPI: Reboot, Not supported!\n"); 121 } 122 123 ASSERT_NOT_REACHED(); /// If rebooting didn't work, halt. 124} 125 126void ACPIStaticParser::do_acpi_shutdown() 127{ 128 kprintf("ACPI: Shutdown is not supported with the current configuration, Abort!\n"); 129 ASSERT_NOT_REACHED(); 130} 131 132inline bool validate_acpi_table(ACPI_RAW::SDTHeader& v_header, size_t length) 133{ 134 u8 checksum = 0; 135 auto* sdt = (u8*)&v_header; 136 for (size_t i = 0; i < length; i++) 137 checksum += sdt[i]; 138 if (checksum == 0) 139 return true; 140 return false; 141} 142 143size_t ACPIStaticParser::get_table_size(PhysicalAddress table_header) 144{ 145 InterruptDisabler disabler; 146#ifdef ACPI_DEBUG 147 dbgprintf("ACPI: Checking SDT Length\n"); 148#endif 149 auto region = MM.allocate_kernel_region(table_header.page_base(), (PAGE_SIZE * 2), "ACPI get_table_size()", Region::Access::Read); 150 auto* sdt = (volatile ACPI_RAW::SDTHeader*)region->vaddr().offset(table_header.offset_in_page().get()).as_ptr(); 151 return sdt->length; 152} 153 154u8 ACPIStaticParser::get_table_revision(PhysicalAddress table_header) 155{ 156 InterruptDisabler disabler; 157#ifdef ACPI_DEBUG 158 dbgprintf("ACPI: Checking SDT Revision\n"); 159#endif 160 auto region = MM.allocate_kernel_region(table_header.page_base(), (PAGE_SIZE * 2), "ACPI get_table_revision()", Region::Access::Read); 161 auto* sdt = (volatile ACPI_RAW::SDTHeader*)region->vaddr().offset(table_header.offset_in_page().get()).as_ptr(); 162 return sdt->revision; 163} 164 165void ACPIStaticParser::initialize_main_system_description_table() 166{ 167#ifdef ACPI_DEBUG 168 dbgprintf("ACPI: Checking Main SDT Length to choose the correct mapping size\n"); 169#endif 170 ASSERT(!m_main_system_description_table.is_null()); 171 auto length = get_table_size(m_main_system_description_table); 172 auto revision = get_table_revision(m_main_system_description_table); 173 174 auto main_sdt_region = MM.allocate_kernel_region(m_main_system_description_table.page_base(), PAGE_ROUND_UP(length) + PAGE_SIZE, "ACPI Static Parser Initialization", Region::Access::Read, false, true); 175 auto* sdt = (volatile ACPI_RAW::SDTHeader*)main_sdt_region->vaddr().offset(m_main_system_description_table.offset_in_page().get()).as_ptr(); 176 kprintf("ACPI: Main Description Table valid? 0x%x\n", validate_acpi_table(const_cast<ACPI_RAW::SDTHeader&>(*sdt), length)); 177 178 Vector<ACPI_RAW::SDTHeader*> sdt_pointers; 179 if (m_xsdt_supported) { 180 volatile auto* xsdt = (volatile ACPI_RAW::XSDT*)sdt; 181 kprintf("ACPI: Using XSDT, Enumerating tables @ P 0x%x\n", m_main_system_description_table.get()); 182 kprintf("ACPI: XSDT Revision %d, Total length - %u\n", revision, length); 183#ifdef ACPI_DEBUG 184 dbgprintf("ACPI: XSDT pointer @ V 0x%x\n", xsdt); 185#endif 186 for (u32 i = 0; i < ((length - sizeof(ACPI_RAW::SDTHeader)) / sizeof(u64)); i++) { 187#ifdef ACPI_DEBUG 188 dbgprintf("ACPI: Found new table [%u], @ V0x%x - P0x%x\n", i, &xsdt->table_ptrs[i], xsdt->table_ptrs[i]); 189#endif 190 sdt_pointers.append((ACPI_RAW::SDTHeader*)xsdt->table_ptrs[i]); 191 } 192 } else { 193 volatile auto* rsdt = (volatile ACPI_RAW::RSDT*)sdt; 194 kprintf("ACPI: Using RSDT, Enumerating tables @ P 0x%x\n", m_main_system_description_table.get()); 195 kprintf("ACPI: RSDT Revision %d, Total length - %u\n", revision, length); 196#ifdef ACPI_DEBUG 197 dbgprintf("ACPI: RSDT pointer @ V 0x%x\n", rsdt); 198#endif 199 for (u32 i = 0; i < ((length - sizeof(ACPI_RAW::SDTHeader)) / sizeof(u32)); i++) { 200#ifdef ACPI_DEBUG 201 dbgprintf("ACPI: Found new table [%u], @ V0x%x - P0x%x\n", i, &rsdt->table_ptrs[i], rsdt->table_ptrs[i]); 202#endif 203 sdt_pointers.append((ACPI_RAW::SDTHeader*)rsdt->table_ptrs[i]); 204 } 205 } 206 m_main_sdt = OwnPtr<ACPI::MainSystemDescriptionTable>(new ACPI::MainSystemDescriptionTable(move(sdt_pointers))); 207} 208 209void ACPIStaticParser::locate_main_system_description_table() 210{ 211 auto rsdp_region = MM.allocate_kernel_region(m_rsdp.page_base(), (PAGE_SIZE * 2), "ACPI Static Parser Initialization", Region::Access::Read, false, true); 212 volatile auto* rsdp = (ACPI_RAW::RSDPDescriptor20*)rsdp_region->vaddr().offset(m_rsdp.offset_in_page().get()).as_ptr(); 213 if (rsdp->base.revision == 0) { 214 m_xsdt_supported = false; 215 } else if (rsdp->base.revision >= 2) { 216 if (rsdp->xsdt_ptr != (u64) nullptr) { 217 m_xsdt_supported = true; 218 } else { 219 m_xsdt_supported = false; 220 } 221 } 222 if (!m_xsdt_supported) { 223 m_main_system_description_table = PhysicalAddress(rsdp->base.rsdt_ptr); 224 } else { 225 m_main_system_description_table = PhysicalAddress(rsdp->xsdt_ptr); 226 } 227} 228 229void ACPIStaticParser::locate_all_aml_tables() 230{ 231 // Note: According to the ACPI spec, DSDT pointer may be found in the FADT table. 232 // All other continuation of the DSDT can be found as pointers in the RSDT/XSDT. 233 234 kprintf("ACPI: Searching for AML Tables\n"); 235 m_aml_tables_ptrs.append(m_fadt->get_dsdt()); 236 for (auto* sdt_ptr : m_main_sdt->get_sdt_pointers()) { 237 auto region = MM.allocate_kernel_region(PhysicalAddress(page_base_of(sdt_ptr)), (PAGE_SIZE * 2), "ACPI Static Parser AML Tables Finding", Region::Access::Read); 238 auto* sdt = (ACPI_RAW::SDTHeader*)region->vaddr().offset(offset_in_page(sdt_ptr)).as_ptr(); 239#ifdef ACPI_DEBUG 240 dbgprintf("ACPI: Examining Table @ P 0x%x\n", sdt_ptr); 241#endif 242 if (!strncmp(sdt->sig, "SSDT", 4)) { 243 kprintf("ACPI: Found AML Table @ P 0x%x, registering\n", sdt_ptr); 244 m_aml_tables_ptrs.append(sdt); 245 } 246 } 247} 248 249ACPIStaticParser::ACPIStaticParser() 250 : ACPIParser(true) 251 , m_rsdp(search_rsdp()) 252{ 253 if (!m_rsdp.is_null()) { 254 kprintf("ACPI: Using RSDP @ P 0x%x\n", m_rsdp); 255 m_operable = true; 256 locate_static_data(); 257 } else { 258 m_operable = false; 259 kprintf("ACPI: Disabled, due to RSDP being absent\n"); 260 } 261} 262 263PhysicalAddress ACPIStaticParser::search_rsdp_in_ebda(u16 ebda_segment) 264{ 265 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); 266 char* p_rsdp_str = (char*)(PhysicalAddress(ebda_segment << 4).as_ptr()); 267 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) { 268#ifdef ACPI_DEBUG 269 dbgprintf("ACPI: Looking for RSDP in EBDA @ V0x%x, P0x%x\n", rsdp_str, p_rsdp_str); 270#endif 271 if (!strncmp("RSD PTR ", rsdp_str, strlen("RSD PTR "))) 272 return PhysicalAddress((uintptr_t)p_rsdp_str); 273 p_rsdp_str += 16; 274 } 275 return {}; 276} 277 278PhysicalAddress ACPIStaticParser::search_rsdp_in_bios_area() 279{ 280 auto rsdp_region = MM.allocate_kernel_region(PhysicalAddress(page_base_of((u32)0xE0000)), PAGE_ROUND_UP(0xFFFFF - 0xE0000), "ACPI Static Parser RSDP Finding #2", Region::Access::Read, false, true); 281 char* p_rsdp_str = (char*)(PhysicalAddress(0xE0000).as_ptr()); 282 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) { 283#ifdef ACPI_DEBUG 284 dbgprintf("ACPI: Looking for RSDP in BIOS area @ V0x%x, P0x%x\n", rsdp_str, p_rsdp_str); 285#endif 286 if (!strncmp("RSD PTR ", rsdp_str, strlen("RSD PTR "))) 287 return PhysicalAddress((uintptr_t)p_rsdp_str); 288 p_rsdp_str += 16; 289 } 290 return {}; 291} 292 293PhysicalAddress ACPIStaticParser::search_rsdp() 294{ 295 PhysicalAddress rsdp; 296 auto region = MM.allocate_kernel_region(PhysicalAddress(0), PAGE_SIZE, "ACPI Static Parser RSDP Finding", Region::Access::Read); 297 u16 ebda_seg = (u16) * ((uint16_t*)((region->vaddr().get() & PAGE_MASK) + 0x40e)); 298 kprintf("ACPI: Probing EBDA, Segment 0x%x\n", ebda_seg); 299 300 rsdp = search_rsdp_in_ebda(ebda_seg); 301 if (!rsdp.is_null()) 302 return rsdp; 303 return search_rsdp_in_bios_area(); 304} 305 306ACPIStaticParser::ACPIStaticParser(PhysicalAddress rsdp) 307 : ACPIParser(true) 308 , m_rsdp(rsdp) 309{ 310 kprintf("ACPI: Using RSDP @ Px%x\n", rsdp.get()); 311 m_operable = true; 312 locate_static_data(); 313} 314 315ACPI::MainSystemDescriptionTable::MainSystemDescriptionTable(Vector<ACPI_RAW::SDTHeader*>&& sdt_pointers) 316{ 317 for (auto* sdt_ptr : sdt_pointers) { 318#ifdef ACPI_DEBUG 319 dbgprintf("ACPI: Register new table in Main SDT, @ P 0x%x\n", sdt_ptr); 320#endif 321 m_sdt_pointers.append(sdt_ptr); 322 } 323} 324Vector<ACPI_RAW::SDTHeader*>& ACPI::MainSystemDescriptionTable::get_sdt_pointers() 325{ 326 return m_sdt_pointers; 327} 328 329ACPI::FixedACPIData::FixedACPIData(ACPI_RAW::FADT& fadt) 330{ 331 m_dsdt_ptr = fadt.dsdt_ptr; 332#ifdef ACPI_DEBUG 333 dbgprintf("ACPI: DSDT pointer @ P 0x%x\n", m_dsdt_ptr); 334#endif 335 m_revision = fadt.h.revision; 336 m_x_dsdt_ptr = fadt.x_dsdt; 337 m_preferred_pm_profile = fadt.preferred_pm_profile; 338 m_sci_int = fadt.sci_int; 339 m_smi_cmd = fadt.smi_cmd; 340 m_acpi_enable_value = fadt.acpi_enable_value; 341 m_acpi_disable_value = fadt.acpi_disable_value; 342 m_s4bios_req = fadt.s4bios_req; 343 m_pstate_cnt = fadt.pstate_cnt; 344 345 m_PM1a_EVT_BLK = fadt.PM1a_EVT_BLK; 346 m_PM1b_EVT_BLK = fadt.PM1b_EVT_BLK; 347 m_PM1a_CNT_BLK = fadt.PM1a_CNT_BLK; 348 m_PM1b_CNT_BLK = fadt.PM1b_CNT_BLK; 349 m_PM2_CNT_BLK = fadt.PM2_CNT_BLK; 350 m_PM_TMR_BLK = fadt.PM_TMR_BLK; 351 m_GPE0_BLK = fadt.GPE0_BLK; 352 m_GPE1_BLK = fadt.GPE1_BLK; 353 m_PM1_EVT_LEN = fadt.PM1_EVT_LEN; 354 m_PM1_CNT_LEN = fadt.PM1_CNT_LEN; 355 m_PM2_CNT_LEN = fadt.PM2_CNT_LEN; 356 m_PM_TMR_LEN = fadt.PM_TMR_LEN; 357 m_GPE0_BLK_LEN = fadt.GPE0_BLK_LEN; 358 m_GPE1_BLK_LEN = fadt.GPE1_BLK_LEN; 359 m_GPE1_BASE = fadt.GPE1_BASE; 360 m_cst_cnt = fadt.cst_cnt; 361 m_P_LVL2_LAT = fadt.P_LVL2_LAT; 362 m_P_LVL3_LAT = fadt.P_LVL3_LAT; 363 m_flush_size = fadt.flush_size; 364 365 m_flush_stride = fadt.flush_stride; 366 m_duty_offset = fadt.duty_offset; 367 m_duty_width = fadt.duty_width; 368 m_day_alrm = fadt.day_alrm; 369 m_mon_alrm = fadt.mon_alrm; 370 m_century = fadt.century; 371 372 m_ia_pc_boot_arch_flags = fadt.ia_pc_boot_arch_flags; 373 m_flags = fadt.flags; 374 375 m_reset_reg = fadt.reset_reg; 376#ifdef ACPI_DEBUG 377 dbgprintf("ACPI: Reset Register @ IO 0x%x\n", m_reset_reg.address); 378 dbgprintf("ACPI: Reset Register Address space %x\n", fadt.reset_reg.address_space); 379#endif 380 m_reset_value = fadt.reset_value; 381#ifdef ACPI_DEBUG 382 dbgprintf("ACPI: Reset Register value @ P 0x%x\n", m_reset_value); 383#endif 384 m_x_pm1a_evt_blk = fadt.x_pm1a_evt_blk; 385 m_x_pm1b_evt_blk = fadt.x_pm1b_evt_blk; 386 m_x_pm1a_cnt_blk = fadt.x_pm1a_cnt_blk; 387 m_x_pm1b_cnt_blk = fadt.x_pm1b_cnt_blk; 388 m_x_pm2_cnt_blk = fadt.x_pm2_cnt_blk; 389 m_x_pm_tmr_blk = fadt.x_pm_tmr_blk; 390 m_x_gpe0_blk = fadt.x_gpe0_blk; 391 m_x_gpe1_blk = fadt.x_gpe1_blk; 392 m_sleep_control = fadt.sleep_control; 393 m_sleep_status = fadt.sleep_status; 394 395 m_hypervisor_vendor_identity = fadt.hypervisor_vendor_identity; 396} 397 398ACPI_RAW::SDTHeader* ACPI::FixedACPIData::get_dsdt() 399{ 400 if (m_x_dsdt_ptr != (uintptr_t) nullptr) 401 return (ACPI_RAW::SDTHeader*)m_x_dsdt_ptr; 402 else { 403 ASSERT((ACPI_RAW::SDTHeader*)m_dsdt_ptr != nullptr); 404 return (ACPI_RAW::SDTHeader*)m_dsdt_ptr; 405 } 406} 407 408}