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,
+};