summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
authorMichael Brown2017-05-23 16:44:22 +0200
committerMichael Brown2017-05-23 19:48:06 +0200
commit356f6c1b64d7a97746d1816cef8ca22bdd8d0b5d (patch)
treebcfdf9da0e520ac48f6fd3b5ddb37f804fde97d9 /src/core
parent[efi] Provide access to ACPI tables (diff)
downloadipxe-356f6c1b64d7a97746d1816cef8ca22bdd8d0b5d.tar.gz
ipxe-356f6c1b64d7a97746d1816cef8ca22bdd8d0b5d.tar.xz
ipxe-356f6c1b64d7a97746d1816cef8ca22bdd8d0b5d.zip
[acpi] Expose ACPI tables via settings mechanism
Allow values to be read from ACPI tables using the syntax ${acpi/<signature>.<index>.0.<offset>.<length>} where <signature> is the ACPI table signature as a 32-bit hexadecimal number (e.g. 0x41504093 for the 'APIC' signature on the MADT), <index> is the index into the array of tables matching this signature, <offset> is the byte offset within the table, and <length> is the field length in bytes. Numeric values are returned in reverse byte order, since ACPI numeric values are usually little-endian. For example: ${acpi/0x41504943.0.0.0.0} - entire MADT table in raw hex ${acpi/0x41504943.0.0.0x0a.6:string} - MADT table OEM ID ${acpi/0x41504943.0.0.0x24.4:uint32} - local APIC address Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/core')
-rw-r--r--src/core/acpi_settings.c161
1 files changed, 161 insertions, 0 deletions
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 <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
+ *
+ * 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 ) {
+ 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,
+};