All of lore.kernel.org
 help / color / mirror / Atom feed
From: Nikos Nikoleris <nikos.nikoleris@arm.com>
To: kvm@vger.kernel.org, kvmarm@lists.linux.dev, andrew.jones@linux.dev
Cc: pbonzini@redhat.com, alexandru.elisei@arm.com,
	ricarkol@google.com, shahuang@redhat.com
Subject: [kvm-unit-tests PATCH v6 19/32] lib/efi: Add support for reading an FDT
Date: Tue, 30 May 2023 17:09:11 +0100	[thread overview]
Message-ID: <20230530160924.82158-20-nikos.nikoleris@arm.com> (raw)
In-Reply-To: <20230530160924.82158-1-nikos.nikoleris@arm.com>

This change adds support for reading enviroment variables. To do so it
introduces a new GUID: 97ef3e03-7329-4a6a-b9ba-6c1fdcc5f823 that the
user needs to provide when setting enviroment variables. For example,
to set the path to the fdt a user can execute:

In addition, this change add support for reading the fdt into memory
and providing a pointer to the test through efi_bootinfo_t.

Signed-off-by: Nikos Nikoleris <nikos.nikoleris@arm.com>
---
 lib/linux/efi.h | 101 ++++++++++++++++++++++++++++++++++++++++
 lib/efi.h       |  12 +++++
 lib/efi.c       | 120 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 233 insertions(+)

diff --git a/lib/linux/efi.h b/lib/linux/efi.h
index 9a1cf87b..bb6d5a85 100644
--- a/lib/linux/efi.h
+++ b/lib/linux/efi.h
@@ -12,6 +12,7 @@
 
 #define BITS_PER_LONG 64
 
+#define EFI_ERROR(a)		(((int64_t) a) < 0)
 #define EFI_SUCCESS		0
 #define EFI_LOAD_ERROR		( 1 | (1UL << (BITS_PER_LONG-1)))
 #define EFI_INVALID_PARAMETER	( 2 | (1UL << (BITS_PER_LONG-1)))
@@ -436,6 +437,106 @@ struct efi_loaded_image_64 {
 	efi_status_t		(__efiapi * unload)(efi_handle_t image_handle);
 };
 
+
+typedef struct _efi_simple_file_system_protocol efi_simple_file_system_protocol_t;
+typedef struct _efi_file_protocol efi_file_protocol_t;
+typedef efi_simple_file_system_protocol_t efi_file_io_interface_t;
+typedef efi_file_protocol_t efi_file_t;
+
+typedef efi_status_t efi_simple_file_system_protocol_open_volume(
+	efi_simple_file_system_protocol_t *this,
+	efi_file_protocol_t **root);
+
+#define EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID EFI_GUID(0x964e5b22, 0x6459, 0x11d2, 0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
+
+struct _efi_simple_file_system_protocol {
+	uint64_t						revision;
+	efi_simple_file_system_protocol_open_volume __efiapi	*open_volume;
+};
+
+/* Open modes */
+#define EFI_FILE_MODE_READ    0x0000000000000001ULL
+#define EFI_FILE_MODE_WRITE   0x0000000000000002ULL
+#define EFI_FILE_MODE_CREATE  0x8000000000000000ULL
+
+typedef efi_status_t efi_file_open(efi_file_protocol_t *this,
+				   efi_file_protocol_t **new_handle,
+				   efi_char16_t *file_name,
+				   uint64_t open_mode,
+				   uint64_t attributes);
+typedef efi_status_t efi_file_close(efi_file_protocol_t *this);
+typedef efi_status_t efi_file_delete(efi_file_protocol_t *this);
+typedef efi_status_t efi_file_read(efi_file_protocol_t *this,
+				   uint64_t *buffer_size,
+				   void *buffer);
+typedef efi_status_t efi_file_write(efi_file_protocol_t *this,
+				    uint64_t *buffer_size,
+				    void *buffer);
+typedef efi_status_t efi_file_set_position(efi_file_protocol_t *this,
+					   uint64_t position);
+typedef efi_status_t efi_file_get_position(efi_file_protocol_t *this,
+					   uint64_t *position);
+typedef efi_status_t efi_file_get_info(efi_file_protocol_t *this,
+				       efi_guid_t *information_type,
+				       uint64_t *buffer_size,
+				       void *buffer);
+typedef efi_status_t efi_file_set_info(efi_file_protocol_t *this,
+				       efi_guid_t *information_type,
+				       uint64_t *buffer_size,
+				       void *buffer);
+typedef efi_status_t efi_file_flush(efi_file_protocol_t *this);
+
+typedef struct {
+	efi_event_t event;
+	efi_status_t status;
+	uint64_t buffer_size;
+	void *buffer;
+} efi_file_io_open_t;
+
+typedef efi_status_t efi_file_open_ex(efi_file_protocol_t *this,
+				      efi_file_protocol_t **new_handle,
+				      efi_char16_t *file_name,
+				      uint64_t open_mode,
+				      uint64_t attributes,
+				      efi_file_io_open_t *token);
+typedef efi_status_t efi_file_read_ex(efi_file_protocol_t *this,
+				      efi_file_io_open_t *token);
+typedef efi_status_t efi_file_write_ex(efi_file_protocol_t *this,
+				       efi_file_io_open_t *token);
+typedef efi_status_t efi_file_flush_ex(efi_file_protocol_t *this,
+				       efi_file_io_open_t *token);
+
+struct _efi_file_protocol {
+	uint64_t			revision;
+	efi_file_open __efiapi		*open;
+	efi_file_close __efiapi		*close;
+	efi_file_delete __efiapi	*delete;
+	efi_file_read __efiapi		*read;
+	efi_file_write __efiapi		*write;
+	efi_file_get_position __efiapi	*get_position;
+	efi_file_set_position __efiapi	*set_position;
+	efi_file_get_info __efiapi	*get_info;
+	efi_file_set_info __efiapi	*set_info;
+	efi_file_flush __efiapi		*flush;
+	efi_file_open_ex __efiapi	*open_ex;
+	efi_file_read_ex __efiapi	*read_ex;
+	efi_file_write_ex __efiapi	*write_ex;
+	efi_file_flush_ex __efiapi	*flush_ex;
+};
+
+#define EFI_FILE_INFO_ID EFI_GUID(0x9576e92, 0x6d3f, 0x11d2, 0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b )
+
+typedef struct {
+	uint64_t	size;
+	uint64_t	file_size;
+	uint64_t	physical_size;
+	efi_time_t	create_time;
+	efi_time_t	last_access_time;
+	efi_time_t	modification_time;
+	uint64_t	attributes;
+	efi_char16_t	file_name[1];
+} efi_file_info_t;
+
 #define efi_bs_call(func, ...) efi_system_table->boottime->func(__VA_ARGS__)
 #define efi_rs_call(func, ...) efi_system_table->runtime->func(__VA_ARGS__)
 
diff --git a/lib/efi.h b/lib/efi.h
index ce8b74d7..db46d450 100644
--- a/lib/efi.h
+++ b/lib/efi.h
@@ -11,6 +11,17 @@
 #include "linux/efi.h"
 #include <elf.h>
 
+/*
+ * Define a GUID that we can use to to pass environment variables.
+ *
+ * For example, to set the variable var to the value val via the EFI shell:
+ * # setvar env -guid 97ef3e03-7329-4a6a-b9ba-6c1fdcc5f823 -rt =L"val"
+ */
+#define EFI_VAR_GUID EFI_GUID(0x97ef3e03, 0x7329, 0x4a6a, 0xb9, 0xba, 0x6c, 0x1f, 0xdc, 0xc5, 0xf8, 0x23);
+
+/* Names of environment variables we can handle */
+#define ENV_VARNAME_DTBFILE L"fdtfile"
+
 /*
  * efi_bootinfo_t: stores EFI-related machine info retrieved before exiting EFI
  * boot services, and is then used by setup_efi(). setup_efi() cannot retrieve
@@ -19,6 +30,7 @@
  */
 typedef struct {
 	struct efi_boot_memmap mem_map;
+	const void *fdt;
 } efi_bootinfo_t;
 
 efi_status_t _relocate(long ldbase, Elf64_Dyn *dyn, efi_handle_t handle,
diff --git a/lib/efi.c b/lib/efi.c
index f524ec9b..2e127a40 100644
--- a/lib/efi.c
+++ b/lib/efi.c
@@ -173,6 +173,125 @@ static char *efi_convert_cmdline(struct efi_loaded_image_64 *image, int *cmd_lin
 	return (char *)cmdline_addr;
 }
 
+/*
+ * Open the file and read it into a buffer.
+ */
+static void efi_load_image(efi_handle_t handle, struct efi_loaded_image_64 *image, void **data,
+			   int *datasize, efi_char16_t *path_name)
+{
+	uint64_t buffer_size = sizeof(efi_file_info_t);
+	efi_file_info_t *file_info;
+	efi_file_io_interface_t *io_if;
+	efi_file_t *root, *file;
+	efi_status_t status;
+	efi_guid_t file_system_proto_guid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
+	efi_guid_t file_info_guid = EFI_FILE_INFO_ID;
+
+	/* Open the device */
+	status = efi_bs_call(handle_protocol, image->device_handle, &file_system_proto_guid,
+			     (void **)&io_if);
+	if (status != EFI_SUCCESS)
+		return;
+
+	status = io_if->open_volume(io_if, &root);
+	if (status != EFI_SUCCESS)
+		return;
+
+	/* And then open the file */
+	status = root->open(root, &file, path_name, EFI_FILE_MODE_READ, 0);
+	if (status != EFI_SUCCESS) {
+		printf("Failed to open %ls - %lx\n", path_name, status);
+		assert(status == EFI_SUCCESS);
+	}
+
+	/* Find the file size in order to allocate the buffer */
+	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, buffer_size, (void **)&file_info);
+	if (status != EFI_SUCCESS)
+		return;
+
+	status = file->get_info(file, &file_info_guid, &buffer_size, file_info);
+	if (status == EFI_BUFFER_TOO_SMALL) {
+		efi_free_pool(file_info);
+		status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, buffer_size, (void **)&file_info);
+		assert(file_info);
+		status = file->get_info(file, &file_info_guid, &buffer_size, file_info);
+	}
+	assert(status == EFI_SUCCESS);
+
+	buffer_size = file_info->file_size;
+
+	efi_free_pool(file_info);
+
+	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, buffer_size, (void **)data);
+	assert(*data);
+	/* Perform the actual read */
+	status = file->read(file, &buffer_size, *data);
+	if (status == EFI_BUFFER_TOO_SMALL) {
+		efi_free_pool(*data);
+		status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, buffer_size, (void **)data);
+		status = file->read(file, &buffer_size, *data);
+	}
+	assert(status == EFI_SUCCESS);
+
+	*datasize = buffer_size;
+}
+
+static int efi_grow_buffer(efi_status_t *status, void **buffer, uint64_t buffer_size)
+{
+	int try_again;
+
+	if (!*buffer && buffer_size) {
+		*status = EFI_BUFFER_TOO_SMALL;
+	}
+
+	try_again = 0;
+	if (*status == EFI_BUFFER_TOO_SMALL) {
+		if (*buffer)
+			efi_free_pool(*buffer);
+
+		efi_bs_call(allocate_pool, EFI_LOADER_DATA, buffer_size, buffer);
+		if (*buffer) {
+			try_again = 1;
+		} else {
+			*status = EFI_OUT_OF_RESOURCES;
+		}
+	}
+
+	if (!try_again && EFI_ERROR(*status) && *buffer) {
+		efi_free_pool(*buffer);
+		*buffer = NULL;
+	}
+
+	return try_again;
+}
+
+static void* efi_get_var(efi_handle_t handle, struct efi_loaded_image_64 *image, efi_char16_t *var)
+{
+	efi_status_t status = EFI_SUCCESS;
+	void *val = NULL;
+	uint64_t val_size = 100;
+	efi_guid_t efi_var_guid = EFI_VAR_GUID;
+
+	while (efi_grow_buffer(&status, &val, val_size))
+		status = efi_rs_call(get_variable, var, &efi_var_guid, NULL, &val_size, val);
+
+	return val;
+}
+
+static void *efi_get_fdt(efi_handle_t handle, struct efi_loaded_image_64 *image)
+{
+	efi_char16_t var[] = ENV_VARNAME_DTBFILE;
+	efi_char16_t *val;
+	void *fdt = NULL;
+	int fdtsize;
+
+	val = efi_get_var(handle, image, var);
+	if (val)
+		efi_load_image(handle, image, &fdt, &fdtsize, val);
+
+	return fdt;
+}
+
 efi_status_t efi_main(efi_handle_t handle, efi_system_table_t *sys_tab)
 {
 	int ret;
@@ -211,6 +330,7 @@ efi_status_t efi_main(efi_handle_t handle, efi_system_table_t *sys_tab)
 	}
 	setup_args(cmdline_ptr);
 
+	efi_bootinfo.fdt = efi_get_fdt(handle, image);
 	/* Set up efi_bootinfo */
 	efi_bootinfo.mem_map.map = &map;
 	efi_bootinfo.mem_map.map_size = &map_size;
-- 
2.25.1


  parent reply	other threads:[~2023-05-30 16:11 UTC|newest]

Thread overview: 59+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-05-30 16:08 [kvm-unit-tests PATCH v6 00/32] EFI and ACPI support for arm64 Nikos Nikoleris
2023-05-30 16:08 ` [kvm-unit-tests PATCH v6 01/32] lib: Move acpi header and implementation to lib Nikos Nikoleris
2023-05-30 16:08 ` [kvm-unit-tests PATCH v6 02/32] x86: Move x86_64-specific EFI CFLAGS to x86_64 Makefile Nikos Nikoleris
2023-05-30 16:08 ` [kvm-unit-tests PATCH v6 03/32] lib: Apply Lindent to acpi.{c,h} Nikos Nikoleris
2023-05-30 16:08 ` [kvm-unit-tests PATCH v6 04/32] lib: Fix style for acpi.{c,h} Nikos Nikoleris
2023-05-30 16:08 ` [kvm-unit-tests PATCH v6 05/32] lib/acpi: Convert table names to Linux style Nikos Nikoleris
2023-05-30 16:08 ` [kvm-unit-tests PATCH v6 06/32] x86: Avoid references to fields of ACPI tables Nikos Nikoleris
2023-05-30 16:08 ` [kvm-unit-tests PATCH v6 07/32] lib/acpi: Ensure all struct definition for ACPI tables are packed Nikos Nikoleris
2023-05-30 16:09 ` [kvm-unit-tests PATCH v6 08/32] lib/acpi: Add support for the XSDT table Nikos Nikoleris
2023-05-30 16:09 ` [kvm-unit-tests PATCH v6 09/32] lib/acpi: Extend the definition of the FADT table Nikos Nikoleris
2023-05-30 16:09 ` [kvm-unit-tests PATCH v6 10/32] devicetree: Check that fdt is not NULL in dt_available() Nikos Nikoleris
2023-05-30 16:09 ` [kvm-unit-tests PATCH v6 11/32] arm64: Add support for setting up the PSCI conduit through ACPI Nikos Nikoleris
2023-05-30 16:09 ` [kvm-unit-tests PATCH v6 12/32] arm64: Add support for discovering the UART " Nikos Nikoleris
2023-06-08 17:18   ` Nadav Amit
2023-06-08 17:24     ` Nadav Amit
2023-06-09  7:21       ` Andrew Jones
2023-06-09 14:06         ` Nikos Nikoleris
2023-06-09 14:31           ` Andrew Jones
2023-06-10  1:13             ` Nadav Amit
2023-05-30 16:09 ` [kvm-unit-tests PATCH v6 13/32] arm64: Add support for timer initialization " Nikos Nikoleris
2023-05-30 16:09 ` [kvm-unit-tests PATCH v6 14/32] arm64: Add support for cpu " Nikos Nikoleris
2023-05-30 16:09 ` [kvm-unit-tests PATCH v6 15/32] arm64: Add support for gic " Nikos Nikoleris
2023-05-30 16:09 ` [kvm-unit-tests PATCH v6 16/32] lib/printf: Support for precision modifier in printf Nikos Nikoleris
2023-05-30 16:09 ` [kvm-unit-tests PATCH v6 17/32] lib/printf: Add support for printing wide strings Nikos Nikoleris
2023-05-30 16:09 ` [kvm-unit-tests PATCH v6 18/32] lib/efi: Add support for getting the cmdline Nikos Nikoleris
2023-05-30 16:09 ` Nikos Nikoleris [this message]
2023-06-07 16:58   ` [kvm-unit-tests PATCH v6 19/32] lib/efi: Add support for reading an FDT Andrew Jones
2023-06-08  6:55     ` Andrew Jones
2023-06-08  9:48       ` Nikos Nikoleris
2023-05-30 16:09 ` [kvm-unit-tests PATCH v6 20/32] arm/arm64: Rename etext to _etext Nikos Nikoleris
2023-05-30 16:09 ` [kvm-unit-tests PATCH v6 21/32] lib: Avoid ms_abi for calls related to EFI on arm64 Nikos Nikoleris
2023-05-30 16:09 ` [kvm-unit-tests PATCH v6 22/32] arm64: Add a new type of memory type flag MR_F_RESERVED Nikos Nikoleris
2023-05-30 16:09 ` [kvm-unit-tests PATCH v6 23/32] arm64: Add a setup sequence for systems that boot through EFI Nikos Nikoleris
2023-06-10  1:17   ` Nadav Amit
2023-06-12  7:54     ` Andrew Jones
2023-05-30 16:09 ` [kvm-unit-tests PATCH v6 24/32] arm64: Copy code from GNU-EFI Nikos Nikoleris
2023-05-30 16:09 ` [kvm-unit-tests PATCH v6 25/32] arm64: Change GNU-EFI imported code to use defined types Nikos Nikoleris
2023-05-30 16:09 ` [kvm-unit-tests PATCH v6 26/32] arm64: Use code from the gnu-efi when booting with EFI Nikos Nikoleris
2023-05-30 16:09 ` [kvm-unit-tests PATCH v6 27/32] lib: Avoid external dependency in libelf Nikos Nikoleris
2023-05-30 16:09 ` [kvm-unit-tests PATCH v6 28/32] arm64: Add support for efi in Makefile Nikos Nikoleris
2023-06-08 20:41   ` Nadav Amit
2023-06-09  7:36     ` Andrew Jones
2023-06-09 17:14       ` Nadav Amit
2023-05-30 16:09 ` [kvm-unit-tests PATCH v6 29/32] lib: arm: Print test exit status Nikos Nikoleris
2023-05-30 16:09 ` [kvm-unit-tests PATCH v6 30/32] arm64: debug: Make inline assembly symbols global Nikos Nikoleris
2023-05-30 16:09 ` [kvm-unit-tests PATCH v6 31/32] arm64: Add an efi/run script Nikos Nikoleris
2023-05-30 16:09 ` [kvm-unit-tests PATCH v6 32/32] arm64: Use the provided fdt when booting through EFI Nikos Nikoleris
2023-06-07 18:52 ` [kvm-unit-tests PATCH v6 00/32] EFI and ACPI support for arm64 Andrew Jones
2023-06-08  7:01 ` Andrew Jones
2023-06-10  8:32 ` Nadav Amit
2023-06-12  7:52   ` Andrew Jones
2023-06-12  9:52     ` Nikos Nikoleris
2023-06-12 10:41       ` Andrew Jones
2023-06-12 10:43         ` Nikos Nikoleris
2023-06-12 15:59       ` Nadav Amit
2023-06-12 21:53         ` Nikos Nikoleris
2023-06-13 11:21           ` Andrew Jones
2023-06-13 12:54             ` Nadav Amit
2023-07-01 12:18 ` Andrew Jones

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230530160924.82158-20-nikos.nikoleris@arm.com \
    --to=nikos.nikoleris@arm.com \
    --cc=alexandru.elisei@arm.com \
    --cc=andrew.jones@linux.dev \
    --cc=kvm@vger.kernel.org \
    --cc=kvmarm@lists.linux.dev \
    --cc=pbonzini@redhat.com \
    --cc=ricarkol@google.com \
    --cc=shahuang@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.