summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Brown2021-03-01 02:38:54 +0100
committerMichael Brown2021-03-01 02:38:54 +0100
commitc09b627973d9362caba39de09f7d2c6990eb9701 (patch)
treed5afad2db96c230f9ab71d0cbb417356c8c06033
parent[acpi] Allow for platforms that provide ACPI tables individually (diff)
downloadipxe-c09b627973d9362caba39de09f7d2c6990eb9701.tar.gz
ipxe-c09b627973d9362caba39de09f7d2c6990eb9701.tar.xz
ipxe-c09b627973d9362caba39de09f7d2c6990eb9701.zip
[linux] Provide ACPI settings via /sys/firmware/acpi/tables
Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r--src/config/defaults/linux.h1
-rw-r--r--src/include/ipxe/acpi.h1
-rw-r--r--src/include/ipxe/linux/linux_acpi.h18
-rw-r--r--src/interface/linux/linux_acpi.c173
4 files changed, 193 insertions, 0 deletions
diff --git a/src/config/defaults/linux.h b/src/config/defaults/linux.h
index 98d2dafe..5c4106d3 100644
--- a/src/config/defaults/linux.h
+++ b/src/config/defaults/linux.h
@@ -21,6 +21,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define REBOOT_NULL
#define PCIAPI_LINUX
#define DMAAPI_FLAT
+#define ACPI_LINUX
#define DRIVERS_LINUX
diff --git a/src/include/ipxe/acpi.h b/src/include/ipxe/acpi.h
index 6d19f05f..81ef7ff7 100644
--- a/src/include/ipxe/acpi.h
+++ b/src/include/ipxe/acpi.h
@@ -360,6 +360,7 @@ extern userptr_t acpi_find_via_rsdt ( uint32_t signature, unsigned int index );
/* Include all architecture-independent ACPI API headers */
#include <ipxe/null_acpi.h>
#include <ipxe/efi/efi_acpi.h>
+#include <ipxe/linux/linux_acpi.h>
/* Include all architecture-dependent ACPI API headers */
#include <bits/acpi.h>
diff --git a/src/include/ipxe/linux/linux_acpi.h b/src/include/ipxe/linux/linux_acpi.h
new file mode 100644
index 00000000..a2c33ce2
--- /dev/null
+++ b/src/include/ipxe/linux/linux_acpi.h
@@ -0,0 +1,18 @@
+#ifndef _IPXE_LINUX_ACPI_H
+#define _IPXE_LINUX_ACPI_H
+
+/** @file
+ *
+ * iPXE ACPI API for Linux
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#ifdef ACPI_LINUX
+#define ACPI_PREFIX_linux
+#else
+#define ACPI_PREFIX_linux __linux_
+#endif
+
+#endif /* _IPXE_LINUX_ACPI_H */
diff --git a/src/interface/linux/linux_acpi.c b/src/interface/linux/linux_acpi.c
new file mode 100644
index 00000000..0f369bda
--- /dev/null
+++ b/src/interface/linux/linux_acpi.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <ipxe/linux_api.h>
+#include <ipxe/linux.h>
+#include <ipxe/list.h>
+#include <ipxe/acpi.h>
+
+/** ACPI sysfs directory */
+#define ACPI_SYSFS_PREFIX "/sys/firmware/acpi/tables/"
+
+/** A cached ACPI table */
+struct linux_acpi_table {
+ /** List of cached tables */
+ struct list_head list;
+ /** Signature */
+ uint32_t signature;
+ /** Index */
+ unsigned int index;
+ /** Cached data */
+ void *data;
+};
+
+/** List of cached ACPI tables */
+static LIST_HEAD ( linux_acpi_tables );
+
+/**
+ * Locate ACPI table
+ *
+ * @v signature Requested table signature
+ * @v index Requested index of table with this signature
+ * @ret table Table, or UNULL if not found
+ */
+static userptr_t linux_acpi_find ( uint32_t signature, unsigned int index ) {
+ struct linux_acpi_table *table;
+ struct acpi_header header;
+ union {
+ uint32_t signature;
+ char filename[5];
+ } u;
+ static const char prefix[] = ACPI_SYSFS_PREFIX;
+ char filename[ sizeof ( prefix ) - 1 /* NUL */ + 4 /* signature */
+ + 3 /* "999" */ + 1 /* NUL */ ];
+ ssize_t rlen;
+ size_t len;
+ void *data;
+ int fd;
+
+ /* Check for existing table */
+ list_for_each_entry ( table, &linux_acpi_tables, list ) {
+ if ( ( table->signature == signature ) &&
+ ( table->index == index ) )
+ return virt_to_user ( table->data );
+ }
+
+ /* Allocate a new table */
+ table = malloc ( sizeof ( *table ) );
+ if ( ! table )
+ goto err_alloc;
+ table->signature = signature;
+ table->index = index;
+
+ /* Construct filename */
+ memset ( &u, 0, sizeof ( u ) );
+ u.signature = le32_to_cpu ( signature );
+ snprintf ( filename, sizeof ( filename ), "%s%s%d", prefix,
+ u.filename, ( index + 1 ) );
+
+ /* Open file */
+ fd = linux_open ( filename, O_RDONLY );
+ if ( ( fd < 0 ) && ( index == 0 ) ) {
+ filename[ sizeof ( prefix ) - 1 /* NUL */ +
+ 4 /* signature */ ] = '\0';
+ fd = linux_open ( filename, O_RDONLY );
+ }
+ if ( fd < 0 ) {
+ DBGC ( &linux_acpi_tables, "ACPI could not open %s: %s\n",
+ filename, linux_strerror ( linux_errno ) );
+ goto err_open;
+ }
+
+ /* Read header */
+ rlen = linux_read ( fd, &header, sizeof ( header ) );
+ if ( rlen < 0 ) {
+ DBGC ( &linux_acpi_tables, "ACPI could not read %s header: "
+ "%s\n", filename, linux_strerror ( linux_errno ) );
+ goto err_header;
+ }
+ if ( ( ( size_t ) rlen ) != sizeof ( header ) ) {
+ DBGC ( &linux_acpi_tables, "ACPI underlength %s header\n",
+ filename );
+ goto err_header_len;
+ }
+
+ /* Parse header */
+ len = le32_to_cpu ( header.length );
+ if ( len < sizeof ( header ) ) {
+ DBGC ( &linux_acpi_tables, "ACPI malformed %s header\n",
+ filename );
+ goto err_malformed;
+ }
+
+ /* Allocate data */
+ table->data = malloc ( len );
+ if ( ! table->data )
+ goto err_data;
+
+ /* Read table */
+ memcpy ( table->data, &header, sizeof ( header ) );
+ data = ( table->data + sizeof ( header ) );
+ len -= sizeof ( header );
+ while ( len ) {
+ rlen = linux_read ( fd, data, len );
+ if ( rlen < 0 ) {
+ DBGC ( &linux_acpi_tables, "ACPI could not read %s: "
+ "%s\n", filename,
+ linux_strerror ( linux_errno ) );
+ goto err_body;
+ }
+ if ( rlen == 0 ) {
+ DBGC ( &linux_acpi_tables, "ACPI underlength %s\n",
+ filename );
+ goto err_body_len;
+ }
+ data += rlen;
+ len -= rlen;
+ }
+
+ /* Close file */
+ linux_close ( fd );
+
+ /* Add to list of tables */
+ list_add ( &table->list, &linux_acpi_tables );
+ DBGC ( &linux_acpi_tables, "ACPI cached %s\n", filename );
+
+ return virt_to_user ( table->data );
+
+ err_body_len:
+ err_body:
+ free ( table->data );
+ err_data:
+ err_malformed:
+ err_header_len:
+ err_header:
+ linux_close ( fd );
+ err_open:
+ free ( table );
+ err_alloc:
+ return UNULL;
+}
+
+PROVIDE_ACPI ( linux, acpi_find, linux_acpi_find );