/* * Copyright (C) 2017 Michael Brown . * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * You can also choose to distribute this program under the terms of * the Unmodified Binary Distribution Licence (as given in the file * COPYING.UBDL), provided that you have satisfied its requirements. */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file * * ACPI Root System Description Pointer * */ #include #include #include #include #include /** EBDA RSDP maximum segment */ #define RSDP_EBDA_END_SEG 0xa000 /** Fixed BIOS area RSDP start address */ #define RSDP_BIOS_START 0xe0000 /** Fixed BIOS area RSDP length */ #define RSDP_BIOS_LEN 0x20000 /** Stride at which to search for RSDP */ #define RSDP_STRIDE 16 /** * Locate ACPI root system description table within a memory range * * @v start Start address to search * @v len Length to search * @ret rsdt ACPI root system description table, or UNULL */ static userptr_t rsdp_find_rsdt_range ( userptr_t start, size_t len ) { static const char signature[8] = RSDP_SIGNATURE; struct acpi_rsdp rsdp; userptr_t rsdt; size_t offset; uint8_t sum; unsigned int i; /* Search for RSDP */ for ( offset = 0 ; ( ( offset + sizeof ( rsdp ) ) < len ) ; offset += RSDP_STRIDE ) { /* Check signature and checksum */ copy_from_user ( &rsdp, start, offset, sizeof ( rsdp ) ); if ( memcmp ( rsdp.signature, signature, sizeof ( signature ) ) != 0 ) continue; for ( sum = 0, i = 0 ; i < sizeof ( rsdp ) ; i++ ) sum += *( ( ( uint8_t * ) &rsdp ) + i ); if ( sum != 0 ) continue; /* Extract RSDT */ rsdt = phys_to_user ( le32_to_cpu ( rsdp.rsdt ) ); DBGC ( rsdt, "RSDT %#08lx found via RSDP %#08lx\n", user_to_phys ( rsdt, 0 ), user_to_phys ( start, offset ) ); return rsdt; } return UNULL; } /** * Locate ACPI root system description table * * @ret rsdt ACPI root system description table, or UNULL */ static userptr_t rsdp_find_rsdt ( void ) { static userptr_t rsdt; uint16_t ebda_seg; userptr_t ebda; size_t ebda_len; /* Return existing RSDT if already found */ if ( rsdt ) return rsdt; /* Search EBDA */ get_real ( ebda_seg, BDA_SEG, BDA_EBDA ); if ( ebda_seg < RSDP_EBDA_END_SEG ) { ebda = real_to_user ( ebda_seg, 0 ); ebda_len = ( ( RSDP_EBDA_END_SEG - ebda_seg ) * 16 ); rsdt = rsdp_find_rsdt_range ( ebda, ebda_len ); if ( rsdt ) return rsdt; } /* Search fixed BIOS area */ rsdt = rsdp_find_rsdt_range ( phys_to_user ( RSDP_BIOS_START ), RSDP_BIOS_LEN ); if ( rsdt ) return rsdt; return UNULL; } PROVIDE_ACPI ( rsdp, acpi_find_rsdt, rsdp_find_rsdt );