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/interface/linux/linux_acpi.c | 173 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 src/interface/linux/linux_acpi.c (limited to 'src/interface/linux/linux_acpi.c') diff --git a/src/interface/linux/linux_acpi.c b/src/interface/linux/linux_acpi.c new file mode 100644 index 000000000..0f369bdaa --- /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.2.3-55-g7522 From 976839ae4c01530a6a931be7b2386a3abee328b9 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 2 Mar 2021 23:56:11 +0000 Subject: [linux] Free cached ACPI tables on shutdown Free any cached ACPI tables for the sake of neatness (and a clean report from Valgrind). Signed-off-by: Michael Brown --- src/interface/linux/linux_acpi.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'src/interface/linux/linux_acpi.c') diff --git a/src/interface/linux/linux_acpi.c b/src/interface/linux/linux_acpi.c index 0f369bdaa..08833d61d 100644 --- a/src/interface/linux/linux_acpi.c +++ b/src/interface/linux/linux_acpi.c @@ -25,6 +25,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include /** ACPI sysfs directory */ @@ -170,4 +171,25 @@ static userptr_t linux_acpi_find ( uint32_t signature, unsigned int index ) { return UNULL; } +/** + * Free cached ACPI data + * + */ +static void linux_acpi_shutdown ( int booting __unused ) { + struct linux_acpi_table *table; + struct linux_acpi_table *tmp; + + list_for_each_entry_safe ( table, tmp, &linux_acpi_tables, list ) { + list_del ( &table->list ); + free ( table->data ); + free ( table ); + } +} + +/** ACPI shutdown function */ +struct startup_fn linux_acpi_startup_fn __startup_fn ( STARTUP_NORMAL ) = { + .name = "linux_acpi", + .shutdown = linux_acpi_shutdown, +}; + PROVIDE_ACPI ( linux, acpi_find, linux_acpi_find ); -- cgit v1.2.3-55-g7522 From 681600680845b5c292ff09ce4748e921adb20d96 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 2 Mar 2021 23:42:36 +0000 Subject: [linux] Use generic sysfs mechanism to read ACPI tables Signed-off-by: Michael Brown --- src/interface/linux/linux_acpi.c | 99 +++++++++------------------------------- 1 file changed, 21 insertions(+), 78 deletions(-) (limited to 'src/interface/linux/linux_acpi.c') diff --git a/src/interface/linux/linux_acpi.c b/src/interface/linux/linux_acpi.c index 08833d61d..851cc54e6 100644 --- a/src/interface/linux/linux_acpi.c +++ b/src/interface/linux/linux_acpi.c @@ -23,9 +23,11 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include #include +#include #include /** ACPI sysfs directory */ @@ -40,7 +42,7 @@ struct linux_acpi_table { /** Index */ unsigned int index; /** Cached data */ - void *data; + userptr_t data; }; /** List of cached ACPI tables */ @@ -55,7 +57,6 @@ static LIST_HEAD ( linux_acpi_tables ); */ 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]; @@ -63,16 +64,14 @@ static userptr_t linux_acpi_find ( uint32_t signature, unsigned int index ) { 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; + int len; + int rc; /* 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 ); + return table->data; } /* Allocate a new table */ @@ -82,90 +81,34 @@ static userptr_t linux_acpi_find ( uint32_t signature, unsigned int index ) { table->signature = signature; table->index = index; - /* Construct filename */ + /* Construct filename (including numeric suffix) */ 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 ) ) { + /* Read file (with or without numeric suffix for index 0) */ + len = linux_sysfs_read ( filename, &table->data ); + if ( ( len < 0 ) && ( index == 0 ) ) { filename[ sizeof ( prefix ) - 1 /* NUL */ + 4 /* signature */ ] = '\0'; - fd = linux_open ( filename, O_RDONLY ); + len = linux_sysfs_read ( filename, &table->data ); } - if ( fd < 0 ) { - DBGC ( &linux_acpi_tables, "ACPI could not open %s: %s\n", - filename, linux_strerror ( linux_errno ) ); - goto err_open; + if ( len < 0 ) { + rc = len; + DBGC ( &linux_acpi_tables, "ACPI could not read %s: %s\n", + filename, strerror ( rc ) ); + goto err_read; } - /* 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: + return table->data; + + ufree ( table->data ); + err_read: free ( table ); err_alloc: return UNULL; @@ -181,7 +124,7 @@ static void linux_acpi_shutdown ( int booting __unused ) { list_for_each_entry_safe ( table, tmp, &linux_acpi_tables, list ) { list_del ( &table->list ); - free ( table->data ); + ufree ( table->data ); free ( table ); } } -- cgit v1.2.3-55-g7522 From 1c4917b6a739c887acbf6f7631b1f74084430ee7 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 3 Mar 2021 01:55:07 +0000 Subject: [linux] Validate length of ACPI table read from sysfs Consumers of acpi_find() will assume that returned structures include a valid table header and that the length in the table header is correct. These assumptions are necessary when dealing with raw ACPI tables, since there exists no independent source of length information. Ensure that these assumptions are also valid for ACPI tables read from sysfs. Signed-off-by: Michael Brown --- src/include/ipxe/errfile.h | 1 + src/interface/linux/linux_acpi.c | 10 ++++++++++ 2 files changed, 11 insertions(+) (limited to 'src/interface/linux/linux_acpi.c') diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 3daf7bde7..e3bf9f565 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -389,6 +389,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_efi_autoexec ( ERRFILE_OTHER | 0x00540000 ) #define ERRFILE_efi_cachedhcp ( ERRFILE_OTHER | 0x00550000 ) #define ERRFILE_linux_sysfs ( ERRFILE_OTHER | 0x00560000 ) +#define ERRFILE_linux_acpi ( ERRFILE_OTHER | 0x00570000 ) /** @} */ diff --git a/src/interface/linux/linux_acpi.c b/src/interface/linux/linux_acpi.c index 851cc54e6..e658936f2 100644 --- a/src/interface/linux/linux_acpi.c +++ b/src/interface/linux/linux_acpi.c @@ -57,6 +57,7 @@ static LIST_HEAD ( linux_acpi_tables ); */ 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]; @@ -100,6 +101,14 @@ static userptr_t linux_acpi_find ( uint32_t signature, unsigned int index ) { filename, strerror ( rc ) ); goto err_read; } + header = user_to_virt ( table->data, 0 ); + if ( ( ( ( size_t ) len ) < sizeof ( *header ) ) || + ( ( ( size_t ) len ) < le32_to_cpu ( header->length ) ) ) { + rc = -ENOENT; + DBGC ( &linux_acpi_tables, "ACPI underlength %s (%d bytes)\n", + filename, len ); + goto err_len; + } /* Add to list of tables */ list_add ( &table->list, &linux_acpi_tables ); @@ -107,6 +116,7 @@ static userptr_t linux_acpi_find ( uint32_t signature, unsigned int index ) { return table->data; + err_len: ufree ( table->data ); err_read: free ( table ); -- cgit v1.2.3-55-g7522