[kvm-unit-tests PATCH v6 19/32] lib/efi: Add support for reading an FDT

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxx>
---
 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




[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux