From: Zixuan Wang <zxwang42@xxxxxxxxx> 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@xxxxxxxxx> --- lib/efi.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++ x86/efi/run | 26 ++++++++++- 2 files changed, 151 insertions(+), 1 deletion(-) 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 = ↦ 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" -- 2.35.1