diff --git a/src/config/config.c b/src/config/config.c index faf9aefa..8adb1ed3 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -337,6 +337,9 @@ REQUIRE_OBJECT ( memmap_settings ); #ifdef VRAM_SETTINGS REQUIRE_OBJECT ( vram_settings ); #endif +#ifdef ACPI_SETTINGS +REQUIRE_OBJECT ( acpi_settings ); +#endif /* * Drag in selected keyboard map diff --git a/src/config/settings.h b/src/config/settings.h index 01feaaa8..d9c86a38 100644 --- a/src/config/settings.h +++ b/src/config/settings.h @@ -14,6 +14,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); //#define MEMMAP_SETTINGS /* Memory map settings */ //#define VMWARE_SETTINGS /* VMware GuestInfo settings */ //#define VRAM_SETTINGS /* Video RAM dump settings */ +//#define ACPI_SETTINGS /* ACPI settings */ #include #include NAMED_CONFIG(settings.h) diff --git a/src/core/acpi_settings.c b/src/core/acpi_settings.c new file mode 100644 index 00000000..7ba2e979 --- /dev/null +++ b/src/core/acpi_settings.c @@ -0,0 +1,161 @@ +/* + * 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 settings + * + */ + +#include +#include +#include +#include +#include + +/** 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 ) { + struct acpi_header acpi; + uint32_t tag_high; + uint32_t tag_low; + uint32_t tag_signature; + unsigned int tag_index; + size_t tag_offset; + size_t tag_len; + userptr_t table; + 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 */ + table = acpi_find ( tag_signature, tag_index ); + if ( ! table ) + return -ENOENT; + + /* Read table header */ + copy_from_user ( &acpi, table, 0, sizeof ( acpi ) ); + + /* 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 */ + for ( i = 0 ; ( ( i < max_len ) && ( i < len ) ) ; i++ ) { + copy_from_user ( data, table, offset, 1 ); + data++; + offset += 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 ) = { + .initialise = acpi_settings_init, +}; diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index cd12ebf3..faa1e77f 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -366,6 +366,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_efi_local ( ERRFILE_OTHER | 0x004d0000 ) #define ERRFILE_efi_entropy ( ERRFILE_OTHER | 0x004e0000 ) #define ERRFILE_cert_cmd ( ERRFILE_OTHER | 0x004f0000 ) +#define ERRFILE_acpi_settings ( ERRFILE_OTHER | 0x00500000 ) /** @} */