From c09b627973d9362caba39de09f7d2c6990eb9701 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 1 Mar 2021 01:38:54 +0000 Subject: [linux] Provide ACPI settings via /sys/firmware/acpi/tables Signed-off-by: Michael Brown --- src/config/defaults/linux.h | 1 + src/include/ipxe/acpi.h | 1 + src/include/ipxe/linux/linux_acpi.h | 18 ++++ src/interface/linux/linux_acpi.c | 173 ++++++++++++++++++++++++++++++++++++ 4 files changed, 193 insertions(+) create mode 100644 src/include/ipxe/linux/linux_acpi.h create mode 100644 src/interface/linux/linux_acpi.c diff --git a/src/config/defaults/linux.h b/src/config/defaults/linux.h index 98d2daf..5c4106d 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 6d19f05..81ef7ff 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 #include +#include /* Include all architecture-dependent ACPI API headers */ #include diff --git a/src/include/ipxe/linux/linux_acpi.h b/src/include/ipxe/linux/linux_acpi.h new file mode 100644 index 0000000..a2c33ce --- /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 0000000..0f369bd --- /dev/null +++ b/src/interface/linux/linux_acpi.c @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2021 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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include +#include + +/** 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 ); -- cgit v1.1