WIP - EFI settings
diff --git a/src/config/config.c b/src/config/config.c
index a818661..329a413 100644
--- a/src/config/config.c
+++ b/src/config/config.c
@@ -352,6 +352,9 @@
#ifdef ACPI_SETTINGS
REQUIRE_OBJECT ( acpi_settings );
#endif
+#ifdef EFI_SETTINGS
+REQUIRE_OBJECT ( efi_settings );
+#endif
/*
* Drag in selected keyboard map
diff --git a/src/config/defaults/efi.h b/src/config/defaults/efi.h
index 8e53b9a..cb9e234 100644
--- a/src/config/defaults/efi.h
+++ b/src/config/defaults/efi.h
@@ -48,6 +48,8 @@
#define REBOOT_CMD /* Reboot command */
+#define EFI_SETTINGS /* EFI variable settings */
+
#if defined ( __i386__ ) || defined ( __x86_64__ )
#define IOAPI_X86
#define NAP_EFIX86
diff --git a/src/config/settings.h b/src/config/settings.h
index d9c86a3..d7f787d 100644
--- a/src/config/settings.h
+++ b/src/config/settings.h
@@ -9,6 +9,8 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+#include <config/defaults.h>
+
#define PCI_SETTINGS /* PCI device settings */
//#define CPUID_SETTINGS /* CPUID settings */
//#define MEMMAP_SETTINGS /* Memory map settings */
diff --git a/src/include/ipxe/efi/Guid/GlobalVariable.h b/src/include/ipxe/efi/Guid/GlobalVariable.h
new file mode 100644
index 0000000..5a9806c
--- /dev/null
+++ b/src/include/ipxe/efi/Guid/GlobalVariable.h
@@ -0,0 +1,188 @@
+/** @file
+ GUID for EFI (NVRAM) Variables.
+
+ Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Revision Reference:
+ GUID defined in UEFI 2.1
+**/
+
+#ifndef __GLOBAL_VARIABLE_GUID_H__
+#define __GLOBAL_VARIABLE_GUID_H__
+
+FILE_LICENCE ( BSD2_PATENT );
+
+#define EFI_GLOBAL_VARIABLE \
+ { \
+ 0x8BE4DF61, 0x93CA, 0x11d2, {0xAA, 0x0D, 0x00, 0xE0, 0x98, 0x03, 0x2B, 0x8C } \
+ }
+
+extern EFI_GUID gEfiGlobalVariableGuid;
+
+//
+// Follow UEFI 2.4 spec:
+// To prevent name collisions with possible future globally defined variables,
+// other internal firmware data variables that are not defined here must be
+// saved with a unique VendorGuid other than EFI_GLOBAL_VARIABLE or
+// any other GUID defined by the UEFI Specification. Implementations must
+// only permit the creation of variables with a UEFI Specification-defined
+// VendorGuid when these variables are documented in the UEFI Specification.
+//
+// Note: except the globally defined variables defined below, the spec also defines
+// L"Boot####" - A boot load option.
+// L"Driver####" - A driver load option.
+// L"SysPrep####" - A System Prep application load option.
+// L"Key####" - Describes hot key relationship with a Boot#### load option.
+// The attribute for them is NV+BS+RT, #### is a printed hex value, and no 0x or h
+// is included in the hex value. They can not be expressed as a #define like other globally
+// defined variables, it is because we can not list the Boot0000, Boot0001, etc one by one.
+//
+
+///
+/// The language codes that the firmware supports. This value is deprecated.
+/// Its attribute is BS+RT.
+///
+#define EFI_LANG_CODES_VARIABLE_NAME L"LangCodes"
+///
+/// The language code that the system is configured for. This value is deprecated.
+/// Its attribute is NV+BS+RT.
+///
+#define EFI_LANG_VARIABLE_NAME L"Lang"
+///
+/// The firmware's boot managers timeout, in seconds, before initiating the default boot selection.
+/// Its attribute is NV+BS+RT.
+///
+#define EFI_TIME_OUT_VARIABLE_NAME L"Timeout"
+///
+/// The language codes that the firmware supports.
+/// Its attribute is BS+RT.
+///
+#define EFI_PLATFORM_LANG_CODES_VARIABLE_NAME L"PlatformLangCodes"
+///
+/// The language code that the system is configured for.
+/// Its attribute is NV+BS+RT.
+///
+#define EFI_PLATFORM_LANG_VARIABLE_NAME L"PlatformLang"
+///
+/// The device path of the default input/output/error output console.
+/// Its attribute is NV+BS+RT.
+///
+#define EFI_CON_IN_VARIABLE_NAME L"ConIn"
+#define EFI_CON_OUT_VARIABLE_NAME L"ConOut"
+#define EFI_ERR_OUT_VARIABLE_NAME L"ErrOut"
+///
+/// The device path of all possible input/output/error output devices.
+/// Its attribute is BS+RT.
+///
+#define EFI_CON_IN_DEV_VARIABLE_NAME L"ConInDev"
+#define EFI_CON_OUT_DEV_VARIABLE_NAME L"ConOutDev"
+#define EFI_ERR_OUT_DEV_VARIABLE_NAME L"ErrOutDev"
+///
+/// The ordered boot option load list.
+/// Its attribute is NV+BS+RT.
+///
+#define EFI_BOOT_ORDER_VARIABLE_NAME L"BootOrder"
+///
+/// The boot option for the next boot only.
+/// Its attribute is NV+BS+RT.
+///
+#define EFI_BOOT_NEXT_VARIABLE_NAME L"BootNext"
+///
+/// The boot option that was selected for the current boot.
+/// Its attribute is BS+RT.
+///
+#define EFI_BOOT_CURRENT_VARIABLE_NAME L"BootCurrent"
+///
+/// The types of boot options supported by the boot manager. Should be treated as read-only.
+/// Its attribute is BS+RT.
+///
+#define EFI_BOOT_OPTION_SUPPORT_VARIABLE_NAME L"BootOptionSupport"
+///
+/// The ordered driver load option list.
+/// Its attribute is NV+BS+RT.
+///
+#define EFI_DRIVER_ORDER_VARIABLE_NAME L"DriverOrder"
+///
+/// The ordered System Prep Application load option list.
+/// Its attribute is NV+BS+RT.
+///
+#define EFI_SYS_PREP_ORDER_VARIABLE_NAME L"SysPrepOrder"
+///
+/// Identifies the level of hardware error record persistence
+/// support implemented by the platform. This variable is
+/// only modified by firmware and is read-only to the OS.
+/// Its attribute is NV+BS+RT.
+///
+#define EFI_HW_ERR_REC_SUPPORT_VARIABLE_NAME L"HwErrRecSupport"
+///
+/// Whether the system is operating in setup mode (1) or not (0).
+/// All other values are reserved. Should be treated as read-only.
+/// Its attribute is BS+RT.
+///
+#define EFI_SETUP_MODE_NAME L"SetupMode"
+///
+/// The Key Exchange Key Signature Database.
+/// Its attribute is NV+BS+RT+AT.
+///
+#define EFI_KEY_EXCHANGE_KEY_NAME L"KEK"
+///
+/// The public Platform Key.
+/// Its attribute is NV+BS+RT+AT.
+///
+#define EFI_PLATFORM_KEY_NAME L"PK"
+///
+/// Array of GUIDs representing the type of signatures supported
+/// by the platform firmware. Should be treated as read-only.
+/// Its attribute is BS+RT.
+///
+#define EFI_SIGNATURE_SUPPORT_NAME L"SignatureSupport"
+///
+/// Whether the platform firmware is operating in Secure boot mode (1) or not (0).
+/// All other values are reserved. Should be treated as read-only.
+/// Its attribute is BS+RT.
+///
+#define EFI_SECURE_BOOT_MODE_NAME L"SecureBoot"
+///
+/// The OEM's default Key Exchange Key Signature Database. Should be treated as read-only.
+/// Its attribute is BS+RT.
+///
+#define EFI_KEK_DEFAULT_VARIABLE_NAME L"KEKDefault"
+///
+/// The OEM's default public Platform Key. Should be treated as read-only.
+/// Its attribute is BS+RT.
+///
+#define EFI_PK_DEFAULT_VARIABLE_NAME L"PKDefault"
+///
+/// The OEM's default secure boot signature store. Should be treated as read-only.
+/// Its attribute is BS+RT.
+///
+#define EFI_DB_DEFAULT_VARIABLE_NAME L"dbDefault"
+///
+/// The OEM's default secure boot blacklist signature store. Should be treated as read-only.
+/// Its attribute is BS+RT.
+///
+#define EFI_DBX_DEFAULT_VARIABLE_NAME L"dbxDefault"
+///
+/// The OEM's default secure boot timestamp signature store. Should be treated as read-only.
+/// Its attribute is BS+RT.
+///
+#define EFI_DBT_DEFAULT_VARIABLE_NAME L"dbtDefault"
+///
+/// Allows the firmware to indicate supported features and actions to the OS.
+/// Its attribute is BS+RT.
+///
+#define EFI_OS_INDICATIONS_SUPPORT_VARIABLE_NAME L"OsIndicationsSupported"
+///
+/// Allows the OS to request the firmware to enable certain features and to take certain actions.
+/// Its attribute is NV+BS+RT.
+///
+#define EFI_OS_INDICATIONS_VARIABLE_NAME L"OsIndications"
+///
+/// Whether the system is configured to use only vendor provided
+/// keys or not. Should be treated as read-only.
+/// Its attribute is BS+RT.
+///
+#define EFI_VENDOR_KEYS_VARIABLE_NAME L"VendorKeys"
+
+#endif
diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h
index e6fd852..a540ab2 100644
--- a/src/include/ipxe/errfile.h
+++ b/src/include/ipxe/errfile.h
@@ -404,6 +404,7 @@
#define ERRFILE_dhe ( ERRFILE_OTHER | 0x005a0000 )
#define ERRFILE_efi_cmdline ( ERRFILE_OTHER | 0x005b0000 )
#define ERRFILE_efi_rng ( ERRFILE_OTHER | 0x005c0000 )
+#define ERRFILE_efi_settings ( ERRFILE_OTHER | 0x005d0000 )
/** @} */
diff --git a/src/interface/efi/efi_settings.c b/src/interface/efi/efi_settings.c
new file mode 100644
index 0000000..652396b
--- /dev/null
+++ b/src/interface/efi/efi_settings.c
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2023 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
+ *
+ * EFI variable settings
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ipxe/settings.h>
+#include <ipxe/init.h>
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/Guid/GlobalVariable.h>
+
+/** An EFI variable settings block */
+struct efivar_settings {
+ /** Settings block */
+ struct settings settings;
+ /** Vendor GUID */
+ EFI_GUID guid;
+};
+
+/** EFI settings scope */
+static const struct settings_scope efivar_scope;
+
+/**
+ * Check applicability of EFI variable setting
+ *
+ * @v settings Settings block
+ * @v setting Setting
+ * @ret applies Setting applies within this settings block
+ */
+static int efivar_applies ( struct settings *settings __unused,
+ const struct setting *setting ) {
+
+ return ( setting->scope == &efivar_scope );
+}
+
+/**
+ * Fetch value of EFI variable 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 efivar_fetch ( struct settings *settings, struct setting *setting,
+ void *data, size_t len ) {
+ EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices;
+ struct efivar_settings *efivars =
+ container_of ( settings, struct efivar_settings, settings );
+ size_t name_len = strlen ( setting->name );
+ CHAR16 wname[ name_len + 1 /* wNUL */ ];
+ UINT32 attrs;
+ UINTN size;
+ void *buf;
+ unsigned int i;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Convert name to UCS-2 */
+ for ( i = 0 ; i <= name_len ; i++ )
+ wname[i] = setting->name[i];
+
+ /* Get variable length */
+ size = 0;
+ efirc = rs->GetVariable ( wname, &efivars->guid, &attrs, &size, NULL );
+ if ( ( efirc != 0 ) && ( efirc != EFI_BUFFER_TOO_SMALL ) ) {
+ rc = -EEFI ( efirc );
+ DBGC ( efivars, "EFIVAR %s:%s not present: %s\n",
+ efi_guid_ntoa ( &efivars->guid ), setting->name,
+ strerror ( rc ) );
+ goto err_len;
+ }
+
+ /* Allocate temporary buffer, since GetVariable() is not
+ * guaranteed to return partial data for an underlength
+ * buffer.
+ */
+ buf = malloc ( size );
+ if ( ! buf ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+
+ /* Get variable value */
+ if ( ( efirc = rs->GetVariable ( wname, &efivars->guid, &attrs, &size,
+ buf ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( efivars, "EFIVAR %s:%s could not get %zd bytes: %s\n",
+ efi_guid_ntoa ( &efivars->guid ), setting->name,
+ ( ( size_t ) size ), strerror ( rc ) );
+ goto err_get;
+ }
+ DBGC ( efivars, "EFIVAR %s:%s:\n", efi_guid_ntoa ( &efivars->guid ),
+ setting->name );
+ DBGC_HDA ( efivars, 0, buf, size );
+
+ /* Return setting value */
+ if ( len > size )
+ len = size;
+ memcpy ( data, buf, len );
+ if ( ! setting->type )
+ setting->type = &setting_type_hex;
+
+ /* Free temporary buffer */
+ free ( buf );
+
+ return size;
+
+ err_get:
+ free ( buf );
+ err_alloc:
+ err_len:
+ return rc;
+}
+
+/** EFI variable settings operations */
+static struct settings_operations efivar_operations = {
+ .applies = efivar_applies,
+ .fetch = efivar_fetch,
+};
+
+/** Well-known EFI variable settings blocks */
+static struct efivar_settings efivar_settings[] = {
+ { .settings = { .name = "efi" }, .guid = EFI_GLOBAL_VARIABLE },
+};
+
+/**
+ * Initialise EFI variable settings
+ *
+ */
+static void efivar_init ( void ) {
+ struct efivar_settings *efivars;
+ const char *name;
+ unsigned int i;
+ int rc;
+
+ /* Register all settings blocks */
+ for ( i = 0 ; i < ( sizeof ( efivar_settings ) /
+ sizeof ( efivar_settings[0] ) ) ; i++ ) {
+
+ /* Initialise settings block */
+ efivars = &efivar_settings[i];
+ settings_init ( &efivars->settings, &efivar_operations, NULL,
+ &efivar_scope );
+
+ /* Register settings block */
+ name = efivars->settings.name;
+ if ( ( rc = register_settings ( &efivars->settings, NULL,
+ name ) ) != 0 ) {
+ DBGC ( &efivar_settings, "EFIVAR could not register "
+ "%s: %s\n", name, strerror ( rc ) );
+ /* Continue trying to register remaining blocks */
+ }
+ }
+}
+
+/** EFI variable settings initialiser */
+struct init_fn efivar_init_fn __init_fn ( INIT_NORMAL ) = {
+ .initialise = efivar_init,
+};