]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[acpi] Expose ACPI tables via settings mechanism
authorMichael Brown <mcb30@ipxe.org>
Tue, 23 May 2017 14:44:22 +0000 (15:44 +0100)
committerMichael Brown <mcb30@ipxe.org>
Tue, 23 May 2017 17:48:06 +0000 (18:48 +0100)
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>
src/config/config.c
src/config/settings.h
src/core/acpi_settings.c [new file with mode: 0644]
src/include/ipxe/errfile.h

index faf9aefa84423b8ebe981a94e0e5b843b0bbac26..8adb1ed3f194e65c450d5e9244bb24211747459e 100644 (file)
@@ -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
index 01feaaa87ff6831bdd1b33ff28b73133cdad037b..d9c86a384ca8ad9fdc554979a0c48369975a494c 100644 (file)
@@ -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 <config/named.h>
 #include NAMED_CONFIG(settings.h)
diff --git a/src/core/acpi_settings.c b/src/core/acpi_settings.c
new file mode 100644 (file)
index 0000000..7ba2e97
--- /dev/null
@@ -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,
+};
index cd12ebf3c43ab270a2de84419e8d81f6597d208e..faa1e77f5b399a91fd0fa1b142d50185d9ddd246 100644 (file)
@@ -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 )
 
 /** @} */