| /* | |
| * Copyright (C) 2017 Michael Brown <mbrown@fensystems.co.uk>. | |
| * | |
| * 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_SECBOOT ( PERMITTED ); | |
| /** | |
| * @file | |
| * | |
| * ACPI settings | |
| * | |
| */ | |
| #include <string.h> | |
| #include <errno.h> | |
| #include <ipxe/init.h> | |
| #include <ipxe/settings.h> | |
| #include <ipxe/acpi.h> | |
| /** ACPI settings scope */ | |
| static const struct settings_scope acpi_settings_scope; | |
| /** | |
| * Check applicability of ACPI setting | |
| * | |
| * @v settings Settings block | |
| * @v setting Setting | |
| * @ret applies Setting applies within this settings block | |
| */ | |
| static int acpi_settings_applies ( struct settings *settings __unused, | |
| const struct setting *setting ) { | |
| return ( setting->scope == &acpi_settings_scope ); | |
| } | |
| /** | |
| * Fetch value of ACPI setting | |
| * | |
| * @v settings Settings block | |
| * @v setting Setting to fetch | |
| * @v data Buffer to fill with setting data | |
| * @v len Length of buffer | |
| * @ret len Length of setting data, or negative error | |
| */ | |
| static int acpi_settings_fetch ( struct settings *settings, | |
| struct setting *setting, | |
| void *data, size_t len ) { | |
| const struct acpi_header *acpi; | |
| const uint8_t *src; | |
| uint8_t *dst; | |
| uint32_t tag_high; | |
| uint32_t tag_low; | |
| uint32_t tag_signature; | |
| unsigned int tag_index; | |
| size_t tag_offset; | |
| size_t tag_len; | |
| size_t offset; | |
| size_t max_len; | |
| int delta; | |
| unsigned int i; | |
| /* Parse settings tag */ | |
| tag_high = ( setting->tag >> 32 ); | |
| tag_low = setting->tag; | |
| tag_signature = bswap_32 ( tag_high ); | |
| tag_index = ( ( tag_low >> 24 ) & 0xff ); | |
| tag_offset = ( ( tag_low >> 8 ) & 0xffff ); | |
| tag_len = ( ( tag_low >> 0 ) & 0xff ); | |
| DBGC ( settings, "ACPI %s.%d offset %#zx length %#zx\n", | |
| acpi_name ( tag_signature ), tag_index, tag_offset, tag_len ); | |
| /* Locate ACPI table */ | |
| acpi = acpi_table ( tag_signature, tag_index ); | |
| if ( ! acpi ) | |
| return -ENOENT; | |
| /* Calculate starting offset and maximum available length */ | |
| max_len = le32_to_cpu ( acpi->length ); | |
| if ( tag_offset > max_len ) | |
| return -ENOENT; | |
| offset = tag_offset; | |
| max_len -= offset; | |
| /* Restrict to requested length, if specified */ | |
| if ( tag_len && ( tag_len < max_len ) ) | |
| max_len = tag_len; | |
| /* Invert endianness for numeric settings */ | |
| if ( setting->type && setting->type->numerate ) { | |
| offset += ( max_len - 1 ); | |
| delta = -1; | |
| } else { | |
| delta = +1; | |
| } | |
| /* Read data */ | |
| src = ( ( ( const void * ) acpi ) + offset ); | |
| dst = data; | |
| for ( i = 0 ; ( ( i < max_len ) && ( i < len ) ) ; i++ ) { | |
| *(dst++) = *src; | |
| src += delta; | |
| } | |
| /* Set type if not already specified */ | |
| if ( ! setting->type ) | |
| setting->type = &setting_type_hexraw; | |
| return max_len; | |
| } | |
| /** ACPI settings operations */ | |
| static struct settings_operations acpi_settings_operations = { | |
| .applies = acpi_settings_applies, | |
| .fetch = acpi_settings_fetch, | |
| }; | |
| /** ACPI settings */ | |
| static struct settings acpi_settings = { | |
| .refcnt = NULL, | |
| .siblings = LIST_HEAD_INIT ( acpi_settings.siblings ), | |
| .children = LIST_HEAD_INIT ( acpi_settings.children ), | |
| .op = &acpi_settings_operations, | |
| .default_scope = &acpi_settings_scope, | |
| }; | |
| /** Initialise ACPI settings */ | |
| static void acpi_settings_init ( void ) { | |
| int rc; | |
| if ( ( rc = register_settings ( &acpi_settings, NULL, | |
| "acpi" ) ) != 0 ) { | |
| DBG ( "ACPI could not register settings: %s\n", | |
| strerror ( rc ) ); | |
| return; | |
| } | |
| } | |
| /** ACPI settings initialiser */ | |
| struct init_fn acpi_settings_init_fn __init_fn ( INIT_NORMAL ) = { | |
| .name = "acpi", | |
| .initialise = acpi_settings_init, | |
| }; |