diff mbox series

[kvm-unit-tests,v1,2/3] x86 UEFI: read envs from file

Message ID 20220220224234.422499-3-zxwang42@gmail.com (mailing list archive)
State New, archived
Headers show
Series x86 UEFI: pass envs and args | expand

Commit Message

Zixuan Wang Feb. 20, 2022, 10:42 p.m. UTC
From: Zixuan Wang <zxwang42@gmail.com>

This commit enables kvm-unit-tests to get envs from the host through
UEFI.

Previously, when kvm-unit-tests runs with Multiboot, it gets a kernel
binary through QEMU's '-kernel' argument and envs through the '-initrd'
argument.

But under UEFI, kvm-unit-tests loads an EFI binary from a disk image
instead of QEMU's '-kernel' argument. In this case, QEMU rejects the
'-initrd' argument without the '-kernel' argument being used, which
makes kvm-unit-tests unable to get an initrd through this argument.

This commit enables the host to pass the initrd through a file:
  1. The host runner script 'x86/efi/run' captures the '-initrd'
     argument and suppress it from QEMU arguments, so QEMU will not
     report the error
  2. The runner script reads the default initrd and the user-specified
     initrd to generate an 'ENVS.TXT' file for each EFI test case
  3. The kvm-unit-tests EFI set up process reads this 'ENVS.TXT' from
     the disk image and pass it to 'setup_env()'

Signed-off-by: Zixuan Wang <zxwang42@gmail.com>
---
 lib/efi.c   | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 x86/efi/run |  26 ++++++++++-
 2 files changed, 151 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/lib/efi.c b/lib/efi.c
index 64cc978..4ddc3a4 100644
--- a/lib/efi.c
+++ b/lib/efi.c
@@ -8,6 +8,7 @@ 
  */
 
 #include "efi.h"
+#include "argv.h"
 #include <libcflat.h>
 #include <asm/setup.h>
 
@@ -96,6 +97,121 @@  static void efi_exit(efi_status_t code)
 	efi_rs_call(reset_system, EFI_RESET_SHUTDOWN, code, 0, NULL);
 }
 
+static efi_status_t efi_get_volume(efi_handle_t handle,
+				   efi_file_protocol_t **volume)
+{
+	efi_guid_t loaded_image_protocol = LOADED_IMAGE_PROTOCOL_GUID;
+	efi_guid_t file_system_protocol = EFI_FILE_SYSTEM_GUID;
+	efi_loaded_image_t *loaded_image = NULL;
+	efi_simple_file_system_protocol_t *io;
+	efi_status_t status;
+
+	status = efi_bs_call(handle_protocol, handle, &loaded_image_protocol,
+			     (void **)&loaded_image);
+	if (status != EFI_SUCCESS) {
+		printf("ERROR: failed to handle loaded image");
+		goto efi_get_volume_error;
+	}
+
+	status = efi_bs_call(handle_protocol, loaded_image->device_handle,
+			     &file_system_protocol, (void **)&io);
+	if (status != EFI_SUCCESS) {
+		printf("ERROR: failed to handle file system protocol");
+		goto efi_get_volume_error;
+	}
+
+	status = io->open_volume(io, volume);
+	if (status != EFI_SUCCESS) {
+		printf("ERROR: failed to open volume");
+		goto efi_get_volume_error;
+	}
+
+	return EFI_SUCCESS;
+
+efi_get_volume_error:
+	printf(" error: 0x%lx\n", status);
+	return EFI_ABORTED;
+}
+
+static efi_status_t efi_read_file(efi_file_protocol_t *volume,
+				  efi_char16_t *file_name,
+				  unsigned long *file_size, char **file_data)
+{
+	efi_guid_t file_info_guid = EFI_FILE_INFO_ID;
+	efi_file_protocol_t *file_handle = NULL;
+	struct finfo file_info;
+	unsigned long file_info_size;
+	efi_status_t status;
+
+	status = volume->open(volume, &file_handle, file_name,
+			      EFI_FILE_MODE_READ, 0);
+	if (status != EFI_SUCCESS) {
+		printf("ERROR: failed to open file");
+		goto efi_read_file_error;
+	}
+
+	file_info_size = sizeof(file_info);
+	status = file_handle->get_info(file_handle, &file_info_guid,
+				       &file_info_size, &file_info);
+	if (status != EFI_SUCCESS) {
+		printf("ERROR: failed to get file info");
+		goto efi_read_file_error;
+	}
+
+	*file_size = file_info.info.file_size;
+	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, *file_size + 1,
+			     (void **)file_data);
+	if (status != EFI_SUCCESS) {
+		printf("ERROR: failed allocate buffer");
+		goto efi_read_file_error;
+	}
+
+	status = file_handle->read(file_handle, file_size, *file_data);
+	if (status != EFI_SUCCESS) {
+		printf("ERROR: failed to read file data");
+		goto efi_read_file_error;
+	}
+
+	status = file_handle->close(file_handle);
+	if (status != EFI_SUCCESS) {
+		printf("ERROR: failed to close file");
+		goto efi_read_file_error;
+	}
+
+	(*file_data)[*file_size] = '\0';
+
+	return EFI_SUCCESS;
+
+efi_read_file_error:
+	/*
+	 * TODO: Current printf does not support wide char (2nd byte of the each
+	 * wide char is always a '\0'), thus only the 1st character is printed.
+	 */
+	printf(" file: %ls, error: 0x%lx\n", file_name, status);
+	return EFI_ABORTED;
+}
+
+static efi_status_t efi_set_up_envs(efi_file_protocol_t *volume)
+{
+	efi_char16_t file_name[] = L"ENVS.TXT";
+	unsigned long file_size;
+	char *file_data = NULL;
+	efi_status_t status;
+
+	status = efi_read_file(volume, file_name, &file_size, &file_data);
+	if (status != EFI_SUCCESS) {
+		printf("Failed to read file\n");
+		goto efi_set_up_envs_error;
+	}
+
+	setup_env(file_data, (int)file_size);
+
+	return EFI_SUCCESS;
+
+efi_set_up_envs_error:
+	return EFI_ABORTED;
+}
+
 efi_status_t efi_main(efi_handle_t handle, efi_system_table_t *sys_tab)
 {
 	int ret;
@@ -103,12 +219,22 @@  efi_status_t efi_main(efi_handle_t handle, efi_system_table_t *sys_tab)
 	efi_bootinfo_t efi_bootinfo;
 
 	efi_system_table = sys_tab;
+	efi_file_protocol_t *volume = NULL;
 
 	/* Memory map struct values */
 	efi_memory_desc_t *map = NULL;
 	unsigned long map_size = 0, desc_size = 0, key = 0, buff_size = 0;
 	u32 desc_ver;
 
+	/* Open env and args files */
+	status = efi_get_volume(handle, &volume);
+	if (status != EFI_SUCCESS) {
+		printf("Failed to get volume\n");
+		goto efi_main_error;
+	}
+
+	efi_set_up_envs(volume);
+
 	/* Set up efi_bootinfo */
 	efi_bootinfo.mem_map.map = &map;
 	efi_bootinfo.mem_map.map_size = &map_size;
diff --git a/x86/efi/run b/x86/efi/run
index ac368a5..f4a5930 100755
--- a/x86/efi/run
+++ b/x86/efi/run
@@ -12,6 +12,7 @@  if [ ! -f config.mak ]; then
 	exit 2
 fi
 source config.mak
+source scripts/arch-run.bash
 
 : "${EFI_SRC:=$(realpath "$(dirname "$0")/../")}"
 : "${EFI_UEFI:=/usr/share/ovmf/OVMF.fd}"
@@ -43,6 +44,29 @@  fi
 mkdir -p "$EFI_CASE_DIR"
 cp "$EFI_SRC/$EFI_CASE.efi" "$EFI_CASE_BINARY"
 
+# Capture -initrd
+efi_qemu_args=""
+efi_qemu_initrd=""
+while [[ -n "$1" ]]; do
+	case "$1" in
+		--initrd|-initrd)
+			shift 1
+			efi_qemu_initrd="$1"
+			shift 1
+		;;
+		*)
+			efi_qemu_args+=" $1"
+			shift 1
+		;;
+	esac
+done
+
+# Create ENVS file
+initrd_create
+KVM_UNIT_TESTS_EFI_ENV="$EFI_TEST/$EFI_CASE/ENVS.TXT"
+mv "$KVM_UNIT_TESTS_ENV" "$KVM_UNIT_TESTS_EFI_ENV"
+[[ -f "$efi_qemu_initrd" ]] && cat "$efi_qemu_initrd" >> "$KVM_UNIT_TESTS_EFI_ENV"
+
 # Run test case with 256MiB QEMU memory. QEMU default memory size is 128MiB.
 # After UEFI boot up and we call `LibMemoryMap()`, the largest consecutive
 # memory region is ~42MiB. Although this is sufficient for many test cases to
@@ -60,5 +84,5 @@  cp "$EFI_SRC/$EFI_CASE.efi" "$EFI_CASE_BINARY"
 	-net none \
 	-nographic \
 	-m 256 \
-	"$@" \
+	${efi_qemu_args} \
 	-smp "$EFI_SMP"