GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
config CMD_BOOTEFI_SELFTEST
|
||||
bool "UEFI unit tests"
|
||||
depends on CMD_BOOTEFI
|
||||
imply FAT
|
||||
imply FAT_WRITE
|
||||
help
|
||||
This adds a UEFI test application to U-Boot that can be executed
|
||||
via the 'bootefi selftest' command. It provides extended tests of
|
||||
the UEFI API implementation.
|
||||
@@ -0,0 +1,98 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# (C) Copyright 2017, Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
|
||||
# This file only gets included with CONFIG_EFI_LOADER set, so all
|
||||
# object inclusion implicitly depends on it
|
||||
|
||||
asflags-y += -DHOST_ARCH="$(HOST_ARCH)"
|
||||
ccflags-y += -DHOST_ARCH="$(HOST_ARCH)"
|
||||
|
||||
CFLAGS_efi_selftest_miniapp_exit.o := $(CFLAGS_EFI) -Os -ffreestanding
|
||||
CFLAGS_REMOVE_efi_selftest_miniapp_exit.o := $(CFLAGS_NON_EFI)
|
||||
CFLAGS_efi_selftest_miniapp_return.o := $(CFLAGS_EFI) -Os -ffreestanding
|
||||
CFLAGS_REMOVE_efi_selftest_miniapp_return.o := $(CFLAGS_NON_EFI)
|
||||
|
||||
obj-y += \
|
||||
efi_selftest.o \
|
||||
efi_selftest_bitblt.o \
|
||||
efi_selftest_config_table.o \
|
||||
efi_selftest_controllers.o \
|
||||
efi_selftest_console.o \
|
||||
efi_selftest_crc32.o \
|
||||
efi_selftest_devicepath_util.o \
|
||||
efi_selftest_events.o \
|
||||
efi_selftest_event_groups.o \
|
||||
efi_selftest_exitbootservices.o \
|
||||
efi_selftest_gop.o \
|
||||
efi_selftest_loaded_image.o \
|
||||
efi_selftest_manageprotocols.o \
|
||||
efi_selftest_memory.o \
|
||||
efi_selftest_open_protocol.o \
|
||||
efi_selftest_register_notify.o \
|
||||
efi_selftest_set_virtual_address_map.o \
|
||||
efi_selftest_snp.o \
|
||||
efi_selftest_textinput.o \
|
||||
efi_selftest_textinputex.o \
|
||||
efi_selftest_textoutput.o \
|
||||
efi_selftest_tpl.o \
|
||||
efi_selftest_util.o \
|
||||
efi_selftest_variables.o \
|
||||
efi_selftest_variables_runtime.o \
|
||||
efi_selftest_watchdog.o
|
||||
|
||||
obj-$(CONFIG_EFI_DEVICE_PATH_TO_TEXT) += efi_selftest_devicepath.o
|
||||
obj-$(CONFIG_EFI_UNICODE_COLLATION_PROTOCOL2) += \
|
||||
efi_selftest_unicode_collation.o
|
||||
|
||||
obj-$(CONFIG_CPU_V7) += efi_selftest_unaligned.o
|
||||
obj-$(CONFIG_EFI_LOADER_HII) += efi_selftest_hii.o
|
||||
obj-$(CONFIG_EFI_GET_TIME) += efi_selftest_rtc.o
|
||||
|
||||
ifeq ($(CONFIG_GENERATE_ACPI_TABLE),)
|
||||
obj-y += efi_selftest_fdt.o
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_BLK)$(CONFIG_PARTITIONS),yy)
|
||||
obj-y += efi_selftest_block_device.o
|
||||
endif
|
||||
|
||||
# TODO: As of v2019.10 the relocation code for the EFI application cannot
|
||||
# be built on ARMv7-M.
|
||||
ifeq ($(CONFIG_CPU_V7M),)
|
||||
|
||||
obj-y += \
|
||||
efi_selftest_exception.o \
|
||||
efi_selftest_loadimage.o \
|
||||
efi_selftest_startimage_exit.o \
|
||||
efi_selftest_startimage_return.o
|
||||
|
||||
targets += \
|
||||
efi_miniapp_file_image_exception.h \
|
||||
efi_miniapp_file_image_exit.h \
|
||||
efi_miniapp_file_image_return.h \
|
||||
efi_selftest_miniapp_exception.efi \
|
||||
efi_selftest_miniapp_exit.efi \
|
||||
efi_selftest_miniapp_return.efi
|
||||
|
||||
$(obj)/efi_miniapp_file_image_exception.h: $(obj)/efi_selftest_miniapp_exception.efi
|
||||
$(obj)/../../tools/file2include $(obj)/efi_selftest_miniapp_exception.efi > \
|
||||
$(obj)/efi_miniapp_file_image_exception.h
|
||||
|
||||
$(obj)/efi_miniapp_file_image_exit.h: $(obj)/efi_selftest_miniapp_exit.efi
|
||||
$(obj)/../../tools/file2include $(obj)/efi_selftest_miniapp_exit.efi > \
|
||||
$(obj)/efi_miniapp_file_image_exit.h
|
||||
|
||||
$(obj)/efi_miniapp_file_image_return.h: $(obj)/efi_selftest_miniapp_return.efi
|
||||
$(obj)/../../tools/file2include $(obj)/efi_selftest_miniapp_return.efi > \
|
||||
$(obj)/efi_miniapp_file_image_return.h
|
||||
|
||||
$(obj)/efi_selftest_loadimage.o: $(obj)/efi_miniapp_file_image_exit.h
|
||||
|
||||
$(obj)/efi_selftest_exception.o: $(obj)/efi_miniapp_file_image_exception.h
|
||||
|
||||
$(obj)/efi_selftest_startimage_exit.o: $(obj)/efi_miniapp_file_image_exit.h
|
||||
|
||||
$(obj)/efi_selftest_startimage_return.o: $(obj)/efi_miniapp_file_image_return.h
|
||||
|
||||
endif
|
||||
@@ -0,0 +1,11 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Library for freestanding binary
|
||||
*
|
||||
* Copyright 2019, Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* GCC requires that freestanding programs provide memcpy(), memmove(),
|
||||
* memset(), and memcmp().
|
||||
*/
|
||||
|
||||
#include "../efi_loader/efi_freestanding.c"
|
||||
@@ -0,0 +1,318 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* EFI efi_selftest
|
||||
*
|
||||
* Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
#include <vsprintf.h>
|
||||
|
||||
/* Constants for test step bitmap */
|
||||
#define EFI_ST_SETUP 1
|
||||
#define EFI_ST_EXECUTE 2
|
||||
#define EFI_ST_TEARDOWN 4
|
||||
|
||||
static const struct efi_system_table *systable;
|
||||
static const struct efi_boot_services *boottime;
|
||||
static const struct efi_runtime_services *runtime;
|
||||
static efi_handle_t handle;
|
||||
static u16 reset_message[] = L"Selftest completed";
|
||||
static int *setup_status;
|
||||
|
||||
/*
|
||||
* Exit the boot services.
|
||||
*
|
||||
* The size of the memory map is determined.
|
||||
* Pool memory is allocated to copy the memory map.
|
||||
* The memory map is copied and the map key is obtained.
|
||||
* The map key is used to exit the boot services.
|
||||
*/
|
||||
void efi_st_exit_boot_services(void)
|
||||
{
|
||||
efi_uintn_t map_size = 0;
|
||||
efi_uintn_t map_key;
|
||||
efi_uintn_t desc_size;
|
||||
u32 desc_version;
|
||||
efi_status_t ret;
|
||||
struct efi_mem_desc *memory_map;
|
||||
|
||||
ret = boottime->get_memory_map(&map_size, NULL, &map_key, &desc_size,
|
||||
&desc_version);
|
||||
if (ret != EFI_BUFFER_TOO_SMALL) {
|
||||
efi_st_error(
|
||||
"GetMemoryMap did not return EFI_BUFFER_TOO_SMALL\n");
|
||||
return;
|
||||
}
|
||||
/* Allocate extra space for newly allocated memory */
|
||||
map_size += sizeof(struct efi_mem_desc);
|
||||
ret = boottime->allocate_pool(EFI_BOOT_SERVICES_DATA, map_size,
|
||||
(void **)&memory_map);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("AllocatePool did not return EFI_SUCCESS\n");
|
||||
return;
|
||||
}
|
||||
ret = boottime->get_memory_map(&map_size, memory_map, &map_key,
|
||||
&desc_size, &desc_version);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("GetMemoryMap did not return EFI_SUCCESS\n");
|
||||
return;
|
||||
}
|
||||
ret = boottime->exit_boot_services(handle, map_key);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("ExitBootServices did not return EFI_SUCCESS\n");
|
||||
return;
|
||||
}
|
||||
efi_st_printc(EFI_WHITE, "\nBoot services terminated\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up a test.
|
||||
*
|
||||
* @test the test to be executed
|
||||
* @failures counter that will be incremented if a failure occurs
|
||||
* @return EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int setup(struct efi_unit_test *test, unsigned int *failures)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!test->setup)
|
||||
return EFI_ST_SUCCESS;
|
||||
efi_st_printc(EFI_LIGHTBLUE, "\nSetting up '%s'\n", test->name);
|
||||
ret = test->setup(handle, systable);
|
||||
if (ret != EFI_ST_SUCCESS) {
|
||||
efi_st_error("Setting up '%s' failed\n", test->name);
|
||||
++*failures;
|
||||
} else {
|
||||
efi_st_printc(EFI_LIGHTGREEN,
|
||||
"Setting up '%s' succeeded\n", test->name);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute a test.
|
||||
*
|
||||
* @test the test to be executed
|
||||
* @failures counter that will be incremented if a failure occurs
|
||||
* @return EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int execute(struct efi_unit_test *test, unsigned int *failures)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!test->execute)
|
||||
return EFI_ST_SUCCESS;
|
||||
efi_st_printc(EFI_LIGHTBLUE, "\nExecuting '%s'\n", test->name);
|
||||
ret = test->execute();
|
||||
if (ret != EFI_ST_SUCCESS) {
|
||||
efi_st_error("Executing '%s' failed\n", test->name);
|
||||
++*failures;
|
||||
} else {
|
||||
efi_st_printc(EFI_LIGHTGREEN,
|
||||
"Executing '%s' succeeded\n", test->name);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tear down a test.
|
||||
*
|
||||
* @test the test to be torn down
|
||||
* @failures counter that will be incremented if a failure occurs
|
||||
* @return EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int teardown(struct efi_unit_test *test, unsigned int *failures)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!test->teardown)
|
||||
return EFI_ST_SUCCESS;
|
||||
efi_st_printc(EFI_LIGHTBLUE, "\nTearing down '%s'\n", test->name);
|
||||
ret = test->teardown();
|
||||
if (ret != EFI_ST_SUCCESS) {
|
||||
efi_st_error("Tearing down '%s' failed\n", test->name);
|
||||
++*failures;
|
||||
} else {
|
||||
efi_st_printc(EFI_LIGHTGREEN,
|
||||
"Tearing down '%s' succeeded\n", test->name);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that a test exists.
|
||||
*
|
||||
* @testname: name of the test
|
||||
* @return: test, or NULL if not found
|
||||
*/
|
||||
static struct efi_unit_test *find_test(const u16 *testname)
|
||||
{
|
||||
struct efi_unit_test *test;
|
||||
|
||||
for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
|
||||
test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
|
||||
if (!efi_st_strcmp_16_8(testname, test->name))
|
||||
return test;
|
||||
}
|
||||
efi_st_printf("\nTest '%ps' not found\n", testname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* List all available tests.
|
||||
*/
|
||||
static void list_all_tests(void)
|
||||
{
|
||||
struct efi_unit_test *test;
|
||||
|
||||
/* List all tests */
|
||||
efi_st_printf("\nAvailable tests:\n");
|
||||
for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
|
||||
test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
|
||||
efi_st_printf("'%s'%s\n", test->name,
|
||||
test->on_request ? " - on request" : "");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute test steps of one phase.
|
||||
*
|
||||
* @testname name of a single selected test or NULL
|
||||
* @phase test phase
|
||||
* @steps steps to execute (mask with bits from EFI_ST_...)
|
||||
* failures returns EFI_ST_SUCCESS if all test steps succeeded
|
||||
*/
|
||||
void efi_st_do_tests(const u16 *testname, unsigned int phase,
|
||||
unsigned int steps, unsigned int *failures)
|
||||
{
|
||||
int i = 0;
|
||||
struct efi_unit_test *test;
|
||||
|
||||
for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
|
||||
test < ll_entry_end(struct efi_unit_test, efi_unit_test);
|
||||
++test, ++i) {
|
||||
if (testname ?
|
||||
efi_st_strcmp_16_8(testname, test->name) : test->on_request)
|
||||
continue;
|
||||
if (test->phase != phase)
|
||||
continue;
|
||||
if (steps & EFI_ST_SETUP)
|
||||
setup_status[i] = setup(test, failures);
|
||||
if (steps & EFI_ST_EXECUTE && setup_status[i] == EFI_ST_SUCCESS)
|
||||
execute(test, failures);
|
||||
if (steps & EFI_ST_TEARDOWN)
|
||||
teardown(test, failures);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute selftest of the EFI API
|
||||
*
|
||||
* This is the main entry point of the EFI selftest application.
|
||||
*
|
||||
* All tests use a driver model and are run in three phases:
|
||||
* setup, execute, teardown.
|
||||
*
|
||||
* A test may be setup and executed at boottime,
|
||||
* it may be setup at boottime and executed at runtime,
|
||||
* or it may be setup and executed at runtime.
|
||||
*
|
||||
* After executing all tests the system is reset.
|
||||
*
|
||||
* @image_handle: handle of the loaded EFI image
|
||||
* @systab: EFI system table
|
||||
*/
|
||||
efi_status_t EFIAPI efi_selftest(efi_handle_t image_handle,
|
||||
struct efi_system_table *systab)
|
||||
{
|
||||
unsigned int failures = 0;
|
||||
const u16 *testname = NULL;
|
||||
struct efi_loaded_image *loaded_image;
|
||||
efi_status_t ret;
|
||||
|
||||
systable = systab;
|
||||
boottime = systable->boottime;
|
||||
runtime = systable->runtime;
|
||||
handle = image_handle;
|
||||
con_out = systable->con_out;
|
||||
con_in = systable->con_in;
|
||||
|
||||
ret = boottime->handle_protocol(image_handle, &efi_guid_loaded_image,
|
||||
(void **)&loaded_image);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Cannot open loaded image protocol\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (loaded_image->load_options)
|
||||
testname = (u16 *)loaded_image->load_options;
|
||||
|
||||
if (testname) {
|
||||
if (!efi_st_strcmp_16_8(testname, "list") ||
|
||||
!find_test(testname)) {
|
||||
list_all_tests();
|
||||
/*
|
||||
* TODO:
|
||||
* Once the Exit boottime service is correctly
|
||||
* implemented we should call
|
||||
* boottime->exit(image_handle, EFI_SUCCESS, 0, NULL);
|
||||
* here, cf.
|
||||
* https://lists.denx.de/pipermail/u-boot/2017-October/308720.html
|
||||
*/
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
efi_st_printc(EFI_WHITE, "\nTesting EFI API implementation\n");
|
||||
|
||||
if (testname)
|
||||
efi_st_printc(EFI_WHITE, "\nSelected test: '%ps'\n", testname);
|
||||
else
|
||||
efi_st_printc(EFI_WHITE, "\nNumber of tests to execute: %u\n",
|
||||
ll_entry_count(struct efi_unit_test,
|
||||
efi_unit_test));
|
||||
|
||||
/* Allocate buffer for setup results */
|
||||
ret = boottime->allocate_pool(EFI_RUNTIME_SERVICES_DATA, sizeof(int) *
|
||||
ll_entry_count(struct efi_unit_test,
|
||||
efi_unit_test),
|
||||
(void **)&setup_status);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Allocate pool failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Execute boottime tests */
|
||||
efi_st_do_tests(testname, EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
EFI_ST_SETUP | EFI_ST_EXECUTE | EFI_ST_TEARDOWN,
|
||||
&failures);
|
||||
|
||||
/* Execute mixed tests */
|
||||
efi_st_do_tests(testname, EFI_SETUP_BEFORE_BOOTTIME_EXIT,
|
||||
EFI_ST_SETUP, &failures);
|
||||
|
||||
efi_st_exit_boot_services();
|
||||
|
||||
efi_st_do_tests(testname, EFI_SETUP_BEFORE_BOOTTIME_EXIT,
|
||||
EFI_ST_EXECUTE | EFI_ST_TEARDOWN, &failures);
|
||||
|
||||
/* Execute runtime tests */
|
||||
efi_st_do_tests(testname, EFI_SETUP_AFTER_BOOTTIME_EXIT,
|
||||
EFI_ST_SETUP | EFI_ST_EXECUTE | EFI_ST_TEARDOWN,
|
||||
&failures);
|
||||
|
||||
/* Give feedback */
|
||||
efi_st_printc(EFI_WHITE, "\nSummary: %u failures\n\n", failures);
|
||||
|
||||
/* Reset system */
|
||||
efi_st_printf("Preparing for reset. Press any key...\n");
|
||||
efi_st_get_key();
|
||||
runtime->reset_system(EFI_RESET_WARM, EFI_NOT_READY,
|
||||
sizeof(reset_message), reset_message);
|
||||
efi_st_printf("\n");
|
||||
efi_st_error("Reset failed\n");
|
||||
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
@@ -0,0 +1,310 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_bitblt
|
||||
*
|
||||
* Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* Test the block image transfer in the graphical output protocol.
|
||||
* An animated submarine is shown.
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
|
||||
#define WIDTH 200
|
||||
#define HEIGHT 120
|
||||
#define DEPTH 60
|
||||
|
||||
static const struct efi_gop_pixel BLACK = { 0, 0, 0, 0};
|
||||
static const struct efi_gop_pixel RED = { 0, 0, 255, 0};
|
||||
static const struct efi_gop_pixel ORANGE = { 0, 128, 255, 0};
|
||||
static const struct efi_gop_pixel YELLOW = { 0, 255, 255, 0};
|
||||
static const struct efi_gop_pixel GREEN = { 0, 255, 0, 0};
|
||||
static const struct efi_gop_pixel DARK_BLUE = {128, 0, 0, 0};
|
||||
static const struct efi_gop_pixel LIGHT_BLUE = {255, 192, 192, 0};
|
||||
|
||||
static struct efi_boot_services *boottime;
|
||||
static efi_guid_t efi_gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
|
||||
static struct efi_gop *gop;
|
||||
static struct efi_gop_pixel *bitmap;
|
||||
static struct efi_event *event;
|
||||
static efi_uintn_t xpos;
|
||||
|
||||
static void ellipse(efi_uintn_t x, efi_uintn_t y,
|
||||
efi_uintn_t x0, efi_uintn_t y0,
|
||||
efi_uintn_t x1, efi_uintn_t y1,
|
||||
const struct efi_gop_pixel col, struct efi_gop_pixel *pix)
|
||||
{
|
||||
efi_uintn_t xm = x0 + x1;
|
||||
efi_uintn_t ym = y0 + y1;
|
||||
efi_uintn_t dx = x1 - x0 + 1;
|
||||
efi_uintn_t dy = y1 - y0 + 1;
|
||||
|
||||
if (dy * dy * (2 * x - xm) * (2 * x - xm) +
|
||||
dx * dx * (2 * y - ym) * (2 * y - ym) <= dx * dx * dy * dy)
|
||||
*pix = col;
|
||||
}
|
||||
|
||||
static void rectangle(efi_uintn_t x, efi_uintn_t y,
|
||||
efi_uintn_t x0, efi_uintn_t y0,
|
||||
efi_uintn_t x1, efi_uintn_t y1,
|
||||
const struct efi_gop_pixel col, struct efi_gop_pixel *pix)
|
||||
{
|
||||
if (x >= x0 && y >= y0 && x <= x1 && y <= y1)
|
||||
*pix = col;
|
||||
}
|
||||
|
||||
/*
|
||||
* Notification function, copies image to video.
|
||||
* The position is incremented in each call.
|
||||
*
|
||||
* @event notified event
|
||||
* @context pointer to the notification count
|
||||
*/
|
||||
static void EFIAPI notify(struct efi_event *event, void *context)
|
||||
{
|
||||
efi_uintn_t *pos = context;
|
||||
efi_uintn_t dx, sx, width;
|
||||
|
||||
if (!pos)
|
||||
return;
|
||||
|
||||
/* Increment position */
|
||||
*pos += 5;
|
||||
if (*pos >= WIDTH + gop->mode->info->width)
|
||||
*pos = 0;
|
||||
|
||||
width = WIDTH;
|
||||
dx = *pos - WIDTH;
|
||||
sx = 0;
|
||||
if (*pos >= gop->mode->info->width) {
|
||||
width = WIDTH + gop->mode->info->width - *pos;
|
||||
} else if (*pos < WIDTH) {
|
||||
dx = 0;
|
||||
sx = WIDTH - *pos;
|
||||
width = *pos;
|
||||
}
|
||||
|
||||
/* Copy image to video */
|
||||
gop->blt(gop, bitmap, EFI_BLT_BUFFER_TO_VIDEO, sx, 0, dx, DEPTH,
|
||||
width, HEIGHT, WIDTH * sizeof(struct efi_gop_pixel));
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup unit test.
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int setup(const efi_handle_t handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
efi_status_t ret;
|
||||
struct efi_gop_pixel pix;
|
||||
efi_uintn_t x, y;
|
||||
|
||||
boottime = systable->boottime;
|
||||
|
||||
/* Create event */
|
||||
ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL,
|
||||
TPL_CALLBACK, notify, (void *)&xpos,
|
||||
&event);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("could not create event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/* Get graphical output protocol */
|
||||
ret = boottime->locate_protocol(&efi_gop_guid, NULL, (void **)&gop);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
gop = NULL;
|
||||
efi_st_printf("Graphical output protocol is not available.\n");
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/* Prepare image of submarine */
|
||||
ret = boottime->allocate_pool(EFI_LOADER_DATA,
|
||||
sizeof(struct efi_gop_pixel) *
|
||||
WIDTH * HEIGHT, (void **)&bitmap);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Out of memory\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
for (y = 0; y < HEIGHT; ++y) {
|
||||
for (x = 0; x < WIDTH; ++x) {
|
||||
pix = DARK_BLUE;
|
||||
|
||||
/* Propeller */
|
||||
ellipse(x, y, 35, 55, 43, 75, BLACK, &pix);
|
||||
ellipse(x, y, 36, 56, 42, 74, LIGHT_BLUE, &pix);
|
||||
|
||||
ellipse(x, y, 35, 75, 43, 95, BLACK, &pix);
|
||||
ellipse(x, y, 36, 76, 42, 94, LIGHT_BLUE, &pix);
|
||||
|
||||
/* Shaft */
|
||||
rectangle(x, y, 35, 73, 100, 77, BLACK, &pix);
|
||||
|
||||
/* Periscope */
|
||||
ellipse(x, y, 120, 10, 160, 50, BLACK, &pix);
|
||||
ellipse(x, y, 121, 11, 159, 59, YELLOW, &pix);
|
||||
ellipse(x, y, 130, 20, 150, 40, BLACK, &pix);
|
||||
ellipse(x, y, 131, 21, 149, 49, DARK_BLUE, &pix);
|
||||
rectangle(x, y, 135, 10, 160, 50, DARK_BLUE, &pix);
|
||||
ellipse(x, y, 132, 10, 138, 20, BLACK, &pix);
|
||||
ellipse(x, y, 133, 11, 139, 19, RED, &pix);
|
||||
|
||||
/* Rudder */
|
||||
ellipse(x, y, 45, 40, 75, 70, BLACK, &pix);
|
||||
ellipse(x, y, 46, 41, 74, 69, ORANGE, &pix);
|
||||
ellipse(x, y, 45, 80, 75, 109, BLACK, &pix);
|
||||
ellipse(x, y, 46, 81, 74, 108, RED, &pix);
|
||||
|
||||
/* Bridge */
|
||||
ellipse(x, y, 100, 30, 120, 50, BLACK, &pix);
|
||||
ellipse(x, y, 101, 31, 119, 49, GREEN, &pix);
|
||||
ellipse(x, y, 140, 30, 160, 50, BLACK, &pix);
|
||||
ellipse(x, y, 141, 31, 159, 49, GREEN, &pix);
|
||||
rectangle(x, y, 110, 30, 150, 50, BLACK, &pix);
|
||||
rectangle(x, y, 110, 31, 150, 50, GREEN, &pix);
|
||||
|
||||
/* Hull */
|
||||
ellipse(x, y, 50, 40, 199, 109, BLACK, &pix);
|
||||
ellipse(x, y, 51, 41, 198, 108, LIGHT_BLUE, &pix);
|
||||
|
||||
/* Port holes */
|
||||
ellipse(x, y, 79, 57, 109, 82, BLACK, &pix);
|
||||
ellipse(x, y, 80, 58, 108, 81, LIGHT_BLUE, &pix);
|
||||
ellipse(x, y, 83, 61, 105, 78, BLACK, &pix);
|
||||
ellipse(x, y, 84, 62, 104, 77, YELLOW, &pix);
|
||||
/*
|
||||
* This port hole is created by copying
|
||||
* ellipse(x, y, 119, 57, 149, 82, BLACK, &pix);
|
||||
* ellipse(x, y, 120, 58, 148, 81, LIGHT_BLUE, &pix);
|
||||
* ellipse(x, y, 123, 61, 145, 78, BLACK, &pix);
|
||||
* ellipse(x, y, 124, 62, 144, 77, YELLOW, &pix);
|
||||
*/
|
||||
ellipse(x, y, 159, 57, 189, 82, BLACK, &pix);
|
||||
ellipse(x, y, 160, 58, 188, 81, LIGHT_BLUE, &pix);
|
||||
ellipse(x, y, 163, 61, 185, 78, BLACK, &pix);
|
||||
ellipse(x, y, 164, 62, 184, 77, YELLOW, &pix);
|
||||
|
||||
bitmap[WIDTH * y + x] = pix;
|
||||
}
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tear down unit test.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int teardown(void)
|
||||
{
|
||||
efi_status_t ret;
|
||||
|
||||
if (bitmap) {
|
||||
ret = boottime->free_pool(bitmap);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("FreePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
}
|
||||
if (event) {
|
||||
ret = boottime->close_event(event);
|
||||
event = NULL;
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("could not close event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
}
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute unit test.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
u32 max_mode;
|
||||
efi_status_t ret;
|
||||
struct efi_gop_mode_info *info;
|
||||
|
||||
if (!gop)
|
||||
return EFI_ST_SUCCESS;
|
||||
|
||||
if (!gop->mode) {
|
||||
efi_st_error("EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE missing\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
info = gop->mode->info;
|
||||
max_mode = gop->mode->max_mode;
|
||||
if (!max_mode) {
|
||||
efi_st_error("No graphical mode available\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/* Fill background */
|
||||
ret = gop->blt(gop, bitmap, EFI_BLT_VIDEO_FILL, 0, 0, 0, 0,
|
||||
info->width, info->height, 0);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("EFI_BLT_VIDEO_FILL failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/* Copy image to video */
|
||||
ret = gop->blt(gop, bitmap, EFI_BLT_BUFFER_TO_VIDEO, 0, 0, 0, DEPTH,
|
||||
WIDTH, HEIGHT, 0);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("EFI_BLT_BUFFER_TO_VIDEO failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/* Copy left port hole */
|
||||
ret = gop->blt(gop, bitmap, EFI_BLT_VIDEO_TO_VIDEO,
|
||||
79, 57 + DEPTH, 119, 57 + DEPTH,
|
||||
31, 26, 0);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("EFI_BLT_VIDEO_TO_VIDEO failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/* Copy port holes back to buffer */
|
||||
ret = gop->blt(gop, bitmap, EFI_BLT_VIDEO_TO_BLT_BUFFER,
|
||||
94, 57 + DEPTH, 94, 57,
|
||||
90, 26, WIDTH * sizeof(struct efi_gop_pixel));
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("EFI_BLT_VIDEO_TO_BLT_BUFFER failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/* Set 250ms timer */
|
||||
xpos = WIDTH;
|
||||
ret = boottime->set_timer(event, EFI_TIMER_PERIODIC, 250000);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Could not set timer\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
con_out->set_cursor_position(con_out, 0, 0);
|
||||
con_out->set_attribute(con_out, EFI_WHITE | EFI_BACKGROUND_BLUE);
|
||||
efi_st_printf("The submarine should have three yellow port holes.\n");
|
||||
efi_st_printf("Press any key to continue");
|
||||
efi_st_get_key();
|
||||
con_out->set_attribute(con_out, EFI_LIGHTGRAY);
|
||||
efi_st_printf("\n");
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(bitblt) = {
|
||||
.name = "block image transfer",
|
||||
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup,
|
||||
.execute = execute,
|
||||
.teardown = teardown,
|
||||
.on_request = true,
|
||||
};
|
||||
@@ -0,0 +1,512 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_block
|
||||
*
|
||||
* Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* This test checks the driver for block IO devices.
|
||||
* A disk image is created in memory.
|
||||
* A handle is created for the new block IO device.
|
||||
* The block I/O protocol is installed on the handle.
|
||||
* ConnectController is used to setup partitions and to install the simple
|
||||
* file protocol.
|
||||
* A known file is read from the file system and verified.
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
#include "efi_selftest_disk_image.h"
|
||||
|
||||
/* Block size of compressed disk image */
|
||||
#define COMPRESSED_DISK_IMAGE_BLOCK_SIZE 8
|
||||
|
||||
/* Binary logarithm of the block size */
|
||||
#define LB_BLOCK_SIZE 9
|
||||
|
||||
static struct efi_boot_services *boottime;
|
||||
|
||||
static const efi_guid_t block_io_protocol_guid = EFI_BLOCK_IO_PROTOCOL_GUID;
|
||||
static const efi_guid_t guid_device_path = EFI_DEVICE_PATH_PROTOCOL_GUID;
|
||||
static const efi_guid_t guid_simple_file_system_protocol =
|
||||
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
|
||||
static const efi_guid_t guid_file_system_info = EFI_FILE_SYSTEM_INFO_GUID;
|
||||
static efi_guid_t guid_vendor =
|
||||
EFI_GUID(0xdbca4c98, 0x6cb0, 0x694d,
|
||||
0x08, 0x72, 0x81, 0x9c, 0x65, 0x0c, 0xb7, 0xb8);
|
||||
|
||||
static struct efi_device_path *dp;
|
||||
|
||||
/* One 8 byte block of the compressed disk image */
|
||||
struct line {
|
||||
size_t addr;
|
||||
char *line;
|
||||
};
|
||||
|
||||
/* Compressed disk image */
|
||||
struct compressed_disk_image {
|
||||
size_t length;
|
||||
struct line lines[];
|
||||
};
|
||||
|
||||
static const struct compressed_disk_image img = EFI_ST_DISK_IMG;
|
||||
|
||||
/* Decompressed disk image */
|
||||
static u8 *image;
|
||||
|
||||
/*
|
||||
* Reset service of the block IO protocol.
|
||||
*
|
||||
* @this block IO protocol
|
||||
* @return status code
|
||||
*/
|
||||
static efi_status_t EFIAPI reset(
|
||||
struct efi_block_io *this,
|
||||
char extended_verification)
|
||||
{
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read service of the block IO protocol.
|
||||
*
|
||||
* @this block IO protocol
|
||||
* @media_id media id
|
||||
* @lba start of the read in logical blocks
|
||||
* @buffer_size number of bytes to read
|
||||
* @buffer target buffer
|
||||
* @return status code
|
||||
*/
|
||||
static efi_status_t EFIAPI read_blocks(
|
||||
struct efi_block_io *this, u32 media_id, u64 lba,
|
||||
efi_uintn_t buffer_size, void *buffer)
|
||||
{
|
||||
u8 *start;
|
||||
|
||||
if ((lba << LB_BLOCK_SIZE) + buffer_size > img.length)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
start = image + (lba << LB_BLOCK_SIZE);
|
||||
|
||||
boottime->copy_mem(buffer, start, buffer_size);
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write service of the block IO protocol.
|
||||
*
|
||||
* @this block IO protocol
|
||||
* @media_id media id
|
||||
* @lba start of the write in logical blocks
|
||||
* @buffer_size number of bytes to read
|
||||
* @buffer source buffer
|
||||
* @return status code
|
||||
*/
|
||||
static efi_status_t EFIAPI write_blocks(
|
||||
struct efi_block_io *this, u32 media_id, u64 lba,
|
||||
efi_uintn_t buffer_size, void *buffer)
|
||||
{
|
||||
u8 *start;
|
||||
|
||||
if ((lba << LB_BLOCK_SIZE) + buffer_size > img.length)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
start = image + (lba << LB_BLOCK_SIZE);
|
||||
|
||||
boottime->copy_mem(start, buffer, buffer_size);
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Flush service of the block IO protocol.
|
||||
*
|
||||
* @this block IO protocol
|
||||
* @return status code
|
||||
*/
|
||||
static efi_status_t EFIAPI flush_blocks(struct efi_block_io *this)
|
||||
{
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decompress the disk image.
|
||||
*
|
||||
* @image decompressed disk image
|
||||
* @return status code
|
||||
*/
|
||||
static efi_status_t decompress(u8 **image)
|
||||
{
|
||||
u8 *buf;
|
||||
size_t i;
|
||||
size_t addr;
|
||||
size_t len;
|
||||
efi_status_t ret;
|
||||
|
||||
ret = boottime->allocate_pool(EFI_LOADER_DATA, img.length,
|
||||
(void **)&buf);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Out of memory\n");
|
||||
return ret;
|
||||
}
|
||||
boottime->set_mem(buf, img.length, 0);
|
||||
|
||||
for (i = 0; ; ++i) {
|
||||
if (!img.lines[i].line)
|
||||
break;
|
||||
addr = img.lines[i].addr;
|
||||
len = COMPRESSED_DISK_IMAGE_BLOCK_SIZE;
|
||||
if (addr + len > img.length)
|
||||
len = img.length - addr;
|
||||
boottime->copy_mem(buf + addr, img.lines[i].line, len);
|
||||
}
|
||||
*image = buf;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct efi_block_io_media media;
|
||||
|
||||
static struct efi_block_io block_io = {
|
||||
.media = &media,
|
||||
.reset = reset,
|
||||
.read_blocks = read_blocks,
|
||||
.write_blocks = write_blocks,
|
||||
.flush_blocks = flush_blocks,
|
||||
};
|
||||
|
||||
/* Handle for the block IO device */
|
||||
static efi_handle_t disk_handle;
|
||||
|
||||
/*
|
||||
* Setup unit test.
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int setup(const efi_handle_t handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
efi_status_t ret;
|
||||
struct efi_device_path_vendor vendor_node;
|
||||
struct efi_device_path end_node;
|
||||
|
||||
boottime = systable->boottime;
|
||||
|
||||
decompress(&image);
|
||||
|
||||
block_io.media->block_size = 1 << LB_BLOCK_SIZE;
|
||||
block_io.media->last_block = img.length >> LB_BLOCK_SIZE;
|
||||
|
||||
ret = boottime->install_protocol_interface(
|
||||
&disk_handle, &block_io_protocol_guid,
|
||||
EFI_NATIVE_INTERFACE, &block_io);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to install block I/O protocol\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
ret = boottime->allocate_pool(EFI_LOADER_DATA,
|
||||
sizeof(struct efi_device_path_vendor) +
|
||||
sizeof(struct efi_device_path),
|
||||
(void **)&dp);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Out of memory\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
vendor_node.dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE;
|
||||
vendor_node.dp.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR;
|
||||
vendor_node.dp.length = sizeof(struct efi_device_path_vendor);
|
||||
|
||||
boottime->copy_mem(&vendor_node.guid, &guid_vendor,
|
||||
sizeof(efi_guid_t));
|
||||
boottime->copy_mem(dp, &vendor_node,
|
||||
sizeof(struct efi_device_path_vendor));
|
||||
end_node.type = DEVICE_PATH_TYPE_END;
|
||||
end_node.sub_type = DEVICE_PATH_SUB_TYPE_END;
|
||||
end_node.length = sizeof(struct efi_device_path);
|
||||
|
||||
boottime->copy_mem((char *)dp + sizeof(struct efi_device_path_vendor),
|
||||
&end_node, sizeof(struct efi_device_path));
|
||||
ret = boottime->install_protocol_interface(&disk_handle,
|
||||
&guid_device_path,
|
||||
EFI_NATIVE_INTERFACE,
|
||||
dp);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("InstallProtocolInterface failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tear down unit test.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int teardown(void)
|
||||
{
|
||||
efi_status_t r = EFI_ST_SUCCESS;
|
||||
|
||||
if (disk_handle) {
|
||||
r = boottime->uninstall_protocol_interface(disk_handle,
|
||||
&guid_device_path,
|
||||
dp);
|
||||
if (r != EFI_SUCCESS) {
|
||||
efi_st_error("Uninstall device path failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
r = boottime->uninstall_protocol_interface(
|
||||
disk_handle, &block_io_protocol_guid,
|
||||
&block_io);
|
||||
if (r != EFI_SUCCESS) {
|
||||
efi_st_todo(
|
||||
"Failed to uninstall block I/O protocol\n");
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
if (image) {
|
||||
r = boottime->free_pool(image);
|
||||
if (r != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to free image\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get length of device path without end tag.
|
||||
*
|
||||
* @dp device path
|
||||
* @return length of device path in bytes
|
||||
*/
|
||||
static efi_uintn_t dp_size(struct efi_device_path *dp)
|
||||
{
|
||||
struct efi_device_path *pos = dp;
|
||||
|
||||
while (pos->type != DEVICE_PATH_TYPE_END)
|
||||
pos = (struct efi_device_path *)((char *)pos + pos->length);
|
||||
return (char *)pos - (char *)dp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute unit test.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
efi_status_t ret;
|
||||
efi_uintn_t no_handles, i, len;
|
||||
efi_handle_t *handles;
|
||||
efi_handle_t handle_partition = NULL;
|
||||
struct efi_device_path *dp_partition;
|
||||
struct efi_simple_file_system_protocol *file_system;
|
||||
struct efi_file_handle *root, *file;
|
||||
struct {
|
||||
struct efi_file_system_info info;
|
||||
u16 label[12];
|
||||
} system_info;
|
||||
efi_uintn_t buf_size;
|
||||
char buf[16] __aligned(ARCH_DMA_MINALIGN);
|
||||
u64 pos;
|
||||
|
||||
/* Connect controller to virtual disk */
|
||||
ret = boottime->connect_controller(disk_handle, NULL, NULL, 1);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to connect controller\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/* Get the handle for the partition */
|
||||
ret = boottime->locate_handle_buffer(
|
||||
BY_PROTOCOL, &guid_device_path, NULL,
|
||||
&no_handles, &handles);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to locate handles\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
len = dp_size(dp);
|
||||
for (i = 0; i < no_handles; ++i) {
|
||||
ret = boottime->open_protocol(handles[i], &guid_device_path,
|
||||
(void **)&dp_partition,
|
||||
NULL, NULL,
|
||||
EFI_OPEN_PROTOCOL_GET_PROTOCOL);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to open device path protocol\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (len >= dp_size(dp_partition))
|
||||
continue;
|
||||
if (memcmp(dp, dp_partition, len))
|
||||
continue;
|
||||
handle_partition = handles[i];
|
||||
break;
|
||||
}
|
||||
ret = boottime->free_pool(handles);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to free pool memory\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (!handle_partition) {
|
||||
efi_st_error("Partition handle not found\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/* Open the simple file system protocol */
|
||||
ret = boottime->open_protocol(handle_partition,
|
||||
&guid_simple_file_system_protocol,
|
||||
(void **)&file_system, NULL, NULL,
|
||||
EFI_OPEN_PROTOCOL_GET_PROTOCOL);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to open simple file system protocol\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/* Open volume */
|
||||
ret = file_system->open_volume(file_system, &root);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to open volume\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
buf_size = sizeof(system_info);
|
||||
ret = root->getinfo(root, &guid_file_system_info, &buf_size,
|
||||
&system_info);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to get file system info\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (system_info.info.block_size != 512) {
|
||||
efi_st_error("Wrong block size %u, expected 512\n",
|
||||
system_info.info.block_size);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (efi_st_strcmp_16_8(system_info.info.volume_label, "U-BOOT TEST")) {
|
||||
efi_st_todo(
|
||||
"Wrong volume label '%ps', expected 'U-BOOT TEST'\n",
|
||||
system_info.info.volume_label);
|
||||
}
|
||||
|
||||
/* Read file */
|
||||
ret = root->open(root, &file, L"hello.txt", EFI_FILE_MODE_READ,
|
||||
0);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to open file\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = file->setpos(file, 1);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("SetPosition failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
buf_size = sizeof(buf) - 1;
|
||||
ret = file->read(file, &buf_size, buf);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to read file\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (buf_size != 12) {
|
||||
efi_st_error("Wrong number of bytes read: %u\n",
|
||||
(unsigned int)buf_size);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (memcmp(buf, "ello world!", 11)) {
|
||||
efi_st_error("Unexpected file content\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = file->getpos(file, &pos);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("GetPosition failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (pos != 13) {
|
||||
efi_st_error("GetPosition returned %u, expected 13\n",
|
||||
(unsigned int)pos);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = file->close(file);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to close file\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_FAT_WRITE
|
||||
/* Write file */
|
||||
ret = root->open(root, &file, L"u-boot.txt", EFI_FILE_MODE_READ |
|
||||
EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to open file\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
buf_size = 7;
|
||||
boottime->set_mem(buf, sizeof(buf), 0);
|
||||
boottime->copy_mem(buf, "U-Boot", buf_size);
|
||||
ret = file->write(file, &buf_size, buf);
|
||||
if (ret != EFI_SUCCESS || buf_size != 7) {
|
||||
efi_st_error("Failed to write file\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = file->getpos(file, &pos);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("GetPosition failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (pos != 7) {
|
||||
efi_st_error("GetPosition returned %u, expected 7\n",
|
||||
(unsigned int)pos);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = file->close(file);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to close file\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/* Verify file */
|
||||
boottime->set_mem(buf, sizeof(buf), 0);
|
||||
ret = root->open(root, &file, L"u-boot.txt", EFI_FILE_MODE_READ,
|
||||
0);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to open file\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
buf_size = sizeof(buf) - 1;
|
||||
ret = file->read(file, &buf_size, buf);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to read file\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (buf_size != 7) {
|
||||
efi_st_error("Wrong number of bytes read: %u\n",
|
||||
(unsigned int)buf_size);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (memcmp(buf, "U-Boot", 7)) {
|
||||
efi_st_error("Unexpected file content %s\n", buf);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = file->close(file);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to close file\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
#else
|
||||
efi_st_todo("CONFIG_FAT_WRITE is not set\n");
|
||||
#endif /* CONFIG_FAT_WRITE */
|
||||
|
||||
/* Close volume */
|
||||
ret = root->close(root);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to close volume\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(blkdev) = {
|
||||
.name = "block device",
|
||||
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup,
|
||||
.execute = execute,
|
||||
.teardown = teardown,
|
||||
};
|
||||
@@ -0,0 +1,267 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_config_tables
|
||||
*
|
||||
* Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* This test checks the following service:
|
||||
* InstallConfigurationTable.
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
#include <u-boot/crc.h>
|
||||
|
||||
static const struct efi_system_table *sys_table;
|
||||
static struct efi_boot_services *boottime;
|
||||
|
||||
static efi_guid_t table_guid =
|
||||
EFI_GUID(0xff1c3f9e, 0x795b, 0x1529, 0xf1, 0x55,
|
||||
0x17, 0x2e, 0x51, 0x6b, 0x49, 0x75);
|
||||
|
||||
/*
|
||||
* Notification function, increments the notification count if parameter
|
||||
* context is provided.
|
||||
*
|
||||
* @event notified event
|
||||
* @context pointer to the notification count
|
||||
*/
|
||||
static void EFIAPI notify(struct efi_event *event, void *context)
|
||||
{
|
||||
unsigned int *count = context;
|
||||
|
||||
if (count)
|
||||
++*count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check CRC32 of a table.
|
||||
*/
|
||||
static int check_table(const void *table)
|
||||
{
|
||||
efi_status_t ret;
|
||||
u32 crc32, res;
|
||||
/* Casting from constant to not constant */
|
||||
struct efi_table_hdr *hdr = (struct efi_table_hdr *)table;
|
||||
|
||||
crc32 = hdr->crc32;
|
||||
/*
|
||||
* Setting the CRC32 of the 'const' table to zero is easier than
|
||||
* copying
|
||||
*/
|
||||
hdr->crc32 = 0;
|
||||
ret = boottime->calculate_crc32(table, hdr->headersize, &res);
|
||||
/* Reset table CRC32 so it stays constant */
|
||||
hdr->crc32 = crc32;
|
||||
if (ret != EFI_ST_SUCCESS) {
|
||||
efi_st_error("CalculateCrc32 failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (res != crc32) {
|
||||
efi_st_error("Incorrect CRC32\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup unit test.
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int setup(const efi_handle_t handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
sys_table = systable;
|
||||
boottime = systable->boottime;
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute unit test.
|
||||
*
|
||||
* A table is installed, updated, removed. The table entry and the
|
||||
* triggering of events is checked.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
efi_status_t ret;
|
||||
unsigned int counter = 0;
|
||||
struct efi_event *event;
|
||||
void *table;
|
||||
const unsigned int tables[2];
|
||||
efi_uintn_t i;
|
||||
efi_uintn_t tabcnt;
|
||||
efi_uintn_t table_count = sys_table->nr_tables;
|
||||
|
||||
ret = boottime->create_event_ex(0, TPL_NOTIFY,
|
||||
notify, (void *)&counter,
|
||||
&table_guid, &event);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to create event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/* Try to delete non-existent table */
|
||||
ret = boottime->install_configuration_table(&table_guid, NULL);
|
||||
if (ret != EFI_NOT_FOUND) {
|
||||
efi_st_error("Failed to detect missing table\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (counter) {
|
||||
efi_st_error("Notification function was called.\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Check if the event was signaled */
|
||||
ret = boottime->check_event(event);
|
||||
if (ret == EFI_SUCCESS) {
|
||||
efi_st_error("Event was signaled on EFI_NOT_FOUND\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (counter != 1) {
|
||||
efi_st_error("Notification function was not called.\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (table_count != sys_table->nr_tables) {
|
||||
efi_st_error("Incorrect table count %u, expected %u\n",
|
||||
(unsigned int)sys_table->nr_tables,
|
||||
(unsigned int)table_count);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/* Install table */
|
||||
ret = boottime->install_configuration_table(&table_guid,
|
||||
(void *)&tables[0]);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to install table\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Check signaled state */
|
||||
ret = boottime->check_event(event);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Event was not signaled on insert\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (++table_count != sys_table->nr_tables) {
|
||||
efi_st_error("Incorrect table count %u, expected %u\n",
|
||||
(unsigned int)sys_table->nr_tables,
|
||||
(unsigned int)table_count);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
table = NULL;
|
||||
for (i = 0; i < sys_table->nr_tables; ++i) {
|
||||
if (!memcmp(&sys_table->tables[i].guid, &table_guid,
|
||||
sizeof(efi_guid_t)))
|
||||
table = sys_table->tables[i].table;
|
||||
}
|
||||
if (!table) {
|
||||
efi_st_error("Installed table not found\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (table != &tables[0]) {
|
||||
efi_st_error("Incorrect table address\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (check_table(sys_table) != EFI_ST_SUCCESS) {
|
||||
efi_st_error("Checking system table\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/* Update table */
|
||||
ret = boottime->install_configuration_table(&table_guid,
|
||||
(void *)&tables[1]);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to update table\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Check signaled state */
|
||||
ret = boottime->check_event(event);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Event was not signaled on update\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (table_count != sys_table->nr_tables) {
|
||||
efi_st_error("Incorrect table count %u, expected %u\n",
|
||||
(unsigned int)sys_table->nr_tables,
|
||||
(unsigned int)table_count);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
table = NULL;
|
||||
tabcnt = 0;
|
||||
for (i = 0; i < sys_table->nr_tables; ++i) {
|
||||
if (!memcmp(&sys_table->tables[i].guid, &table_guid,
|
||||
sizeof(efi_guid_t))) {
|
||||
table = sys_table->tables[i].table;
|
||||
++tabcnt;
|
||||
}
|
||||
}
|
||||
if (!table) {
|
||||
efi_st_error("Installed table not found\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (tabcnt > 1) {
|
||||
efi_st_error("Duplicate table GUID\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (table != &tables[1]) {
|
||||
efi_st_error("Incorrect table address\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (check_table(sys_table) != EFI_ST_SUCCESS) {
|
||||
efi_st_error("Checking system table\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/* Delete table */
|
||||
ret = boottime->install_configuration_table(&table_guid, NULL);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to delete table\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Check signaled state */
|
||||
ret = boottime->check_event(event);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Event was not signaled on delete\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (--table_count != sys_table->nr_tables) {
|
||||
efi_st_error("Incorrect table count %u, expected %u\n",
|
||||
(unsigned int)sys_table->nr_tables,
|
||||
(unsigned int)table_count);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
table = NULL;
|
||||
for (i = 0; i < sys_table->nr_tables; ++i) {
|
||||
if (!memcmp(&sys_table->tables[i].guid, &table_guid,
|
||||
sizeof(efi_guid_t))) {
|
||||
table = sys_table->tables[i].table;
|
||||
}
|
||||
}
|
||||
if (table) {
|
||||
efi_st_error("Wrong table deleted\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
ret = boottime->close_event(event);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to close event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (check_table(sys_table) != EFI_ST_SUCCESS) {
|
||||
efi_st_error("Checking system table\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(configtables) = {
|
||||
.name = "configuration tables",
|
||||
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup,
|
||||
.execute = execute,
|
||||
};
|
||||
@@ -0,0 +1,255 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* EFI efi_selftest
|
||||
*
|
||||
* Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
#include <vsprintf.h>
|
||||
|
||||
struct efi_simple_text_output_protocol *con_out;
|
||||
struct efi_simple_text_input_protocol *con_in;
|
||||
|
||||
/*
|
||||
* Print a MAC address to an u16 string
|
||||
*
|
||||
* @pointer: mac address
|
||||
* @buf: pointer to buffer address
|
||||
* on return position of terminating zero word
|
||||
*/
|
||||
static void mac(void *pointer, u16 **buf)
|
||||
{
|
||||
int i, j;
|
||||
u16 c;
|
||||
u8 *p = (u8 *)pointer;
|
||||
u8 byte;
|
||||
u16 *pos = *buf;
|
||||
|
||||
for (i = 0; i < ARP_HLEN; ++i) {
|
||||
if (i)
|
||||
*pos++ = ':';
|
||||
byte = p[i];
|
||||
for (j = 4; j >= 0; j -= 4) {
|
||||
c = (byte >> j) & 0x0f;
|
||||
c += '0';
|
||||
if (c > '9')
|
||||
c += 'a' - '9' - 1;
|
||||
*pos++ = c;
|
||||
}
|
||||
}
|
||||
*pos = 0;
|
||||
*buf = pos;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print a pointer to an u16 string
|
||||
*
|
||||
* @pointer: pointer
|
||||
* @buf: pointer to buffer address
|
||||
* on return position of terminating zero word
|
||||
*/
|
||||
static void pointer(void *pointer, u16 **buf)
|
||||
{
|
||||
int i;
|
||||
u16 c;
|
||||
uintptr_t p = (uintptr_t)pointer;
|
||||
u16 *pos = *buf;
|
||||
|
||||
for (i = 8 * sizeof(p) - 4; i >= 0; i -= 4) {
|
||||
c = (p >> i) & 0x0f;
|
||||
c += '0';
|
||||
if (c > '9')
|
||||
c += 'a' - '9' - 1;
|
||||
*pos++ = c;
|
||||
}
|
||||
*pos = 0;
|
||||
*buf = pos;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print an unsigned 32bit value as decimal number to an u16 string
|
||||
*
|
||||
* @value: value to be printed
|
||||
* @prec: minimum number of digits to display
|
||||
* @buf: pointer to buffer address
|
||||
* on return position of terminating zero word
|
||||
*/
|
||||
static void uint2dec(u32 value, int prec, u16 **buf)
|
||||
{
|
||||
u16 *pos = *buf;
|
||||
int i;
|
||||
u16 c;
|
||||
u64 f;
|
||||
|
||||
/*
|
||||
* Increment by .5 and multiply with
|
||||
* (2 << 60) / 1,000,000,000 = 0x44B82FA0.9B5A52CC
|
||||
* to move the first digit to bit 60-63.
|
||||
*/
|
||||
f = 0x225C17D0;
|
||||
f += (0x9B5A52DULL * value) >> 28;
|
||||
f += 0x44B82FA0ULL * value;
|
||||
|
||||
for (i = 0; i < 10; ++i) {
|
||||
/* Write current digit */
|
||||
c = f >> 60;
|
||||
if (c || pos != *buf || 10 - i <= prec)
|
||||
*pos++ = c + '0';
|
||||
/* Eliminate current digit */
|
||||
f &= 0xfffffffffffffff;
|
||||
/* Get next digit */
|
||||
f *= 0xaULL;
|
||||
}
|
||||
if (pos == *buf)
|
||||
*pos++ = '0';
|
||||
*pos = 0;
|
||||
*buf = pos;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print a signed 32bit value as decimal number to an u16 string
|
||||
*
|
||||
* @value: value to be printed
|
||||
* @prec: minimum number of digits to display
|
||||
* @buf: pointer to buffer address
|
||||
* on return position of terminating zero word
|
||||
*/
|
||||
static void int2dec(s32 value, int prec, u16 **buf)
|
||||
{
|
||||
u32 u;
|
||||
u16 *pos = *buf;
|
||||
|
||||
if (value < 0) {
|
||||
*pos++ = '-';
|
||||
u = -value;
|
||||
} else {
|
||||
u = value;
|
||||
}
|
||||
uint2dec(u, prec, &pos);
|
||||
*buf = pos;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print a colored formatted string to the EFI console
|
||||
*
|
||||
* @color color, see constants in efi_api.h, use -1 for no color
|
||||
* @fmt format string
|
||||
* @... optional arguments
|
||||
*/
|
||||
void efi_st_printc(int color, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
u16 buf[160];
|
||||
const char *c;
|
||||
u16 *pos = buf;
|
||||
const char *s;
|
||||
u16 *u;
|
||||
int prec;
|
||||
|
||||
va_start(args, fmt);
|
||||
|
||||
if (color >= 0)
|
||||
con_out->set_attribute(con_out, (unsigned long)color);
|
||||
c = fmt;
|
||||
for (; *c; ++c) {
|
||||
switch (*c) {
|
||||
case '\\':
|
||||
++c;
|
||||
switch (*c) {
|
||||
case '\0':
|
||||
--c;
|
||||
break;
|
||||
case 'n':
|
||||
*pos++ = '\n';
|
||||
break;
|
||||
case 'r':
|
||||
*pos++ = '\r';
|
||||
break;
|
||||
case 't':
|
||||
*pos++ = '\t';
|
||||
break;
|
||||
default:
|
||||
*pos++ = *c;
|
||||
}
|
||||
break;
|
||||
case '%':
|
||||
++c;
|
||||
/* Parse precision */
|
||||
if (*c == '.') {
|
||||
++c;
|
||||
prec = *c - '0';
|
||||
++c;
|
||||
} else {
|
||||
prec = 0;
|
||||
}
|
||||
switch (*c) {
|
||||
case '\0':
|
||||
--c;
|
||||
break;
|
||||
case 'd':
|
||||
int2dec(va_arg(args, s32), prec, &pos);
|
||||
break;
|
||||
case 'p':
|
||||
++c;
|
||||
switch (*c) {
|
||||
/* MAC address */
|
||||
case 'm':
|
||||
mac(va_arg(args, void*), &pos);
|
||||
break;
|
||||
|
||||
/* u16 string */
|
||||
case 's':
|
||||
u = va_arg(args, u16*);
|
||||
if (pos > buf) {
|
||||
*pos = 0;
|
||||
con_out->output_string(con_out,
|
||||
buf);
|
||||
}
|
||||
con_out->output_string(con_out, u);
|
||||
pos = buf;
|
||||
break;
|
||||
default:
|
||||
--c;
|
||||
pointer(va_arg(args, void*), &pos);
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
s = va_arg(args, const char *);
|
||||
for (; *s; ++s)
|
||||
*pos++ = *s;
|
||||
break;
|
||||
case 'u':
|
||||
uint2dec(va_arg(args, u32), prec, &pos);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
*pos++ = *c;
|
||||
}
|
||||
}
|
||||
va_end(args);
|
||||
*pos = 0;
|
||||
con_out->output_string(con_out, buf);
|
||||
if (color >= 0)
|
||||
con_out->set_attribute(con_out, EFI_LIGHTGRAY);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads an Unicode character from the input device.
|
||||
*
|
||||
* @return: Unicode character
|
||||
*/
|
||||
u16 efi_st_get_key(void)
|
||||
{
|
||||
struct efi_input_key input_key;
|
||||
efi_status_t ret;
|
||||
|
||||
/* Wait for next key */
|
||||
do {
|
||||
ret = con_in->read_key_stroke(con_in, &input_key);
|
||||
} while (ret == EFI_NOT_READY);
|
||||
return input_key.unicode_char;
|
||||
}
|
||||
@@ -0,0 +1,415 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_controllers
|
||||
*
|
||||
* Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* This unit test checks the following protocol services:
|
||||
* ConnectController, DisconnectController,
|
||||
* InstallProtocol, ReinstallProtocol, UninstallProtocol,
|
||||
* OpenProtocol, CloseProtcol, OpenProtocolInformation
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
|
||||
#define NUMBER_OF_CHILD_CONTROLLERS 4
|
||||
|
||||
static int interface1 = 1;
|
||||
static int interface2 = 2;
|
||||
static struct efi_boot_services *boottime;
|
||||
const efi_guid_t guid_driver_binding_protocol =
|
||||
EFI_DRIVER_BINDING_PROTOCOL_GUID;
|
||||
static efi_guid_t guid_controller =
|
||||
EFI_GUID(0xe6ab1d96, 0x6bff, 0xdb42,
|
||||
0xaa, 0x05, 0xc8, 0x1f, 0x7f, 0x45, 0x26, 0x34);
|
||||
static efi_guid_t guid_child_controller =
|
||||
EFI_GUID(0x1d41f6f5, 0x2c41, 0xddfb,
|
||||
0xe2, 0x9b, 0xb8, 0x0e, 0x2e, 0xe8, 0x3a, 0x85);
|
||||
static efi_handle_t handle_controller;
|
||||
static efi_handle_t handle_child_controller[NUMBER_OF_CHILD_CONTROLLERS];
|
||||
static efi_handle_t handle_driver;
|
||||
|
||||
/*
|
||||
* Count child controllers
|
||||
*
|
||||
* @handle handle on which child controllers are installed
|
||||
* @protocol protocol for which the child controllers were installed
|
||||
* @count number of child controllers
|
||||
* @return status code
|
||||
*/
|
||||
static efi_status_t count_child_controllers(efi_handle_t handle,
|
||||
efi_guid_t *protocol,
|
||||
efi_uintn_t *count)
|
||||
{
|
||||
efi_status_t ret;
|
||||
efi_uintn_t entry_count;
|
||||
struct efi_open_protocol_info_entry *entry_buffer;
|
||||
|
||||
*count = 0;
|
||||
ret = boottime->open_protocol_information(handle, protocol,
|
||||
&entry_buffer, &entry_count);
|
||||
if (ret != EFI_SUCCESS)
|
||||
return ret;
|
||||
if (!entry_count)
|
||||
return EFI_SUCCESS;
|
||||
while (entry_count) {
|
||||
if (entry_buffer[--entry_count].attributes &
|
||||
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER)
|
||||
++*count;
|
||||
}
|
||||
ret = boottime->free_pool(entry_buffer);
|
||||
if (ret != EFI_SUCCESS)
|
||||
efi_st_error("Cannot free buffer\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the driver supports the controller.
|
||||
*
|
||||
* @this driver binding protocol
|
||||
* @controller_handle handle of the controller
|
||||
* @remaining_device_path path specifying the child controller
|
||||
* @return status code
|
||||
*/
|
||||
static efi_status_t EFIAPI supported(
|
||||
struct efi_driver_binding_protocol *this,
|
||||
efi_handle_t controller_handle,
|
||||
struct efi_device_path *remaining_device_path)
|
||||
{
|
||||
efi_status_t ret;
|
||||
void *interface;
|
||||
|
||||
ret = boottime->open_protocol(
|
||||
controller_handle, &guid_controller,
|
||||
&interface, handle_driver,
|
||||
controller_handle, EFI_OPEN_PROTOCOL_BY_DRIVER);
|
||||
switch (ret) {
|
||||
case EFI_ACCESS_DENIED:
|
||||
case EFI_ALREADY_STARTED:
|
||||
return ret;
|
||||
case EFI_SUCCESS:
|
||||
break;
|
||||
default:
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
ret = boottime->close_protocol(
|
||||
controller_handle, &guid_controller,
|
||||
handle_driver, controller_handle);
|
||||
if (ret != EFI_SUCCESS)
|
||||
ret = EFI_UNSUPPORTED;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create child controllers and attach driver.
|
||||
*
|
||||
* @this driver binding protocol
|
||||
* @controller_handle handle of the controller
|
||||
* @remaining_device_path path specifying the child controller
|
||||
* @return status code
|
||||
*/
|
||||
static efi_status_t EFIAPI start(
|
||||
struct efi_driver_binding_protocol *this,
|
||||
efi_handle_t controller_handle,
|
||||
struct efi_device_path *remaining_device_path)
|
||||
{
|
||||
size_t i;
|
||||
efi_status_t ret;
|
||||
void *interface;
|
||||
|
||||
/* Attach driver to controller */
|
||||
ret = boottime->open_protocol(
|
||||
controller_handle, &guid_controller,
|
||||
&interface, handle_driver,
|
||||
controller_handle, EFI_OPEN_PROTOCOL_BY_DRIVER);
|
||||
switch (ret) {
|
||||
case EFI_ACCESS_DENIED:
|
||||
case EFI_ALREADY_STARTED:
|
||||
return ret;
|
||||
case EFI_SUCCESS:
|
||||
break;
|
||||
default:
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
/* Create child controllers */
|
||||
for (i = 0; i < NUMBER_OF_CHILD_CONTROLLERS; ++i) {
|
||||
/* Creating a new handle for the child controller */
|
||||
handle_child_controller[i] = 0;
|
||||
ret = boottime->install_protocol_interface(
|
||||
&handle_child_controller[i], &guid_child_controller,
|
||||
EFI_NATIVE_INTERFACE, NULL);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("InstallProtocolInterface failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->open_protocol(
|
||||
controller_handle, &guid_controller,
|
||||
&interface, handle_child_controller[i],
|
||||
handle_child_controller[i],
|
||||
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("OpenProtocol failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove a single child controller from the parent controller.
|
||||
*
|
||||
* @controller_handle parent controller
|
||||
* @child_handle child controller
|
||||
* @return status code
|
||||
*/
|
||||
static efi_status_t disconnect_child(efi_handle_t controller_handle,
|
||||
efi_handle_t child_handle)
|
||||
{
|
||||
efi_status_t ret;
|
||||
|
||||
ret = boottime->close_protocol(
|
||||
controller_handle, &guid_controller,
|
||||
child_handle, child_handle);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Cannot close protocol\n");
|
||||
return ret;
|
||||
}
|
||||
ret = boottime->uninstall_protocol_interface(
|
||||
child_handle, &guid_child_controller, NULL);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Cannot uninstall protocol interface\n");
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove child controllers and disconnect the controller.
|
||||
*
|
||||
* @this driver binding protocol
|
||||
* @controller_handle handle of the controller
|
||||
* @number_of_children number of child controllers to remove
|
||||
* @child_handle_buffer handles of the child controllers to remove
|
||||
* @return status code
|
||||
*/
|
||||
static efi_status_t EFIAPI stop(
|
||||
struct efi_driver_binding_protocol *this,
|
||||
efi_handle_t controller_handle,
|
||||
size_t number_of_children,
|
||||
efi_handle_t *child_handle_buffer)
|
||||
{
|
||||
efi_status_t ret;
|
||||
efi_uintn_t count;
|
||||
struct efi_open_protocol_info_entry *entry_buffer;
|
||||
|
||||
/* Destroy provided child controllers */
|
||||
if (number_of_children) {
|
||||
efi_uintn_t i;
|
||||
|
||||
for (i = 0; i < number_of_children; ++i) {
|
||||
ret = disconnect_child(controller_handle,
|
||||
child_handle_buffer[i]);
|
||||
if (ret != EFI_SUCCESS)
|
||||
return ret;
|
||||
}
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/* Destroy all children */
|
||||
ret = boottime->open_protocol_information(
|
||||
controller_handle, &guid_controller,
|
||||
&entry_buffer, &count);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("OpenProtocolInformation failed\n");
|
||||
return ret;
|
||||
}
|
||||
while (count) {
|
||||
if (entry_buffer[--count].attributes &
|
||||
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) {
|
||||
ret = disconnect_child(
|
||||
controller_handle,
|
||||
entry_buffer[count].agent_handle);
|
||||
if (ret != EFI_SUCCESS)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
ret = boottime->free_pool(entry_buffer);
|
||||
if (ret != EFI_SUCCESS)
|
||||
efi_st_error("Cannot free buffer\n");
|
||||
|
||||
/* Detach driver from controller */
|
||||
ret = boottime->close_protocol(
|
||||
controller_handle, &guid_controller,
|
||||
handle_driver, controller_handle);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Cannot close protocol\n");
|
||||
return ret;
|
||||
}
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/* Driver binding protocol interface */
|
||||
static struct efi_driver_binding_protocol binding_interface = {
|
||||
supported,
|
||||
start,
|
||||
stop,
|
||||
0xffffffff,
|
||||
NULL,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/*
|
||||
* Setup unit test.
|
||||
*
|
||||
* @handle handle of the loaded image
|
||||
* @systable system table
|
||||
*/
|
||||
static int setup(const efi_handle_t img_handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
efi_status_t ret;
|
||||
|
||||
boottime = systable->boottime;
|
||||
|
||||
/* Create controller handle */
|
||||
ret = boottime->install_protocol_interface(
|
||||
&handle_controller, &guid_controller,
|
||||
EFI_NATIVE_INTERFACE, &interface1);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("InstallProtocolInterface failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Create driver handle */
|
||||
ret = boottime->install_protocol_interface(
|
||||
&handle_driver, &guid_driver_binding_protocol,
|
||||
EFI_NATIVE_INTERFACE, &binding_interface);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("InstallProtocolInterface failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute unit test.
|
||||
*
|
||||
* The number of child controllers is checked after each of the following
|
||||
* actions:
|
||||
*
|
||||
* Connect a controller to a driver.
|
||||
* Disconnect and destroy a child controller.
|
||||
* Disconnect and destroy the remaining child controllers.
|
||||
*
|
||||
* Connect a controller to a driver.
|
||||
* Reinstall the driver protocol on the controller.
|
||||
* Uninstall the driver protocol from the controller.
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
efi_status_t ret;
|
||||
efi_uintn_t count;
|
||||
|
||||
/* Connect controller to driver */
|
||||
ret = boottime->connect_controller(handle_controller, NULL, NULL, 1);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to connect controller\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Check number of child controllers */
|
||||
ret = count_child_controllers(handle_controller, &guid_controller,
|
||||
&count);
|
||||
if (ret != EFI_SUCCESS || count != NUMBER_OF_CHILD_CONTROLLERS) {
|
||||
efi_st_error("Number of children %u != %u\n",
|
||||
(unsigned int)count, NUMBER_OF_CHILD_CONTROLLERS);
|
||||
}
|
||||
/* Destroy second child controller */
|
||||
ret = boottime->disconnect_controller(handle_controller,
|
||||
handle_driver,
|
||||
handle_child_controller[1]);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to disconnect child controller\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Check number of child controllers */
|
||||
ret = count_child_controllers(handle_controller, &guid_controller,
|
||||
&count);
|
||||
if (ret != EFI_SUCCESS || count != NUMBER_OF_CHILD_CONTROLLERS - 1) {
|
||||
efi_st_error("Destroying single child controller failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Destroy remaining child controllers and disconnect controller */
|
||||
ret = boottime->disconnect_controller(handle_controller, NULL, NULL);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to disconnect controller\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Check number of child controllers */
|
||||
ret = count_child_controllers(handle_controller, &guid_controller,
|
||||
&count);
|
||||
if (ret != EFI_SUCCESS || count) {
|
||||
efi_st_error("Destroying child controllers failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/* Connect controller to driver */
|
||||
ret = boottime->connect_controller(handle_controller, NULL, NULL, 1);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to connect controller\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Check number of child controllers */
|
||||
ret = count_child_controllers(handle_controller, &guid_controller,
|
||||
&count);
|
||||
if (ret != EFI_SUCCESS || count != NUMBER_OF_CHILD_CONTROLLERS) {
|
||||
efi_st_error("Number of children %u != %u\n",
|
||||
(unsigned int)count, NUMBER_OF_CHILD_CONTROLLERS);
|
||||
}
|
||||
/* Try to uninstall controller protocol using the wrong interface */
|
||||
ret = boottime->uninstall_protocol_interface(handle_controller,
|
||||
&guid_controller,
|
||||
&interface2);
|
||||
if (ret == EFI_SUCCESS) {
|
||||
efi_st_error(
|
||||
"Interface not checked when uninstalling protocol\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Reinstall controller protocol */
|
||||
ret = boottime->reinstall_protocol_interface(handle_controller,
|
||||
&guid_controller,
|
||||
&interface1,
|
||||
&interface2);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to reinstall protocols\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Check number of child controllers */
|
||||
ret = count_child_controllers(handle_controller, &guid_controller,
|
||||
&count);
|
||||
if (ret != EFI_SUCCESS || count != NUMBER_OF_CHILD_CONTROLLERS) {
|
||||
efi_st_error("Number of children %u != %u\n",
|
||||
(unsigned int)count, NUMBER_OF_CHILD_CONTROLLERS);
|
||||
}
|
||||
/* Uninstall controller protocol */
|
||||
ret = boottime->uninstall_protocol_interface(handle_controller,
|
||||
&guid_controller,
|
||||
&interface2);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to uninstall protocols\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Check number of child controllers */
|
||||
ret = count_child_controllers(handle_controller, &guid_controller,
|
||||
&count);
|
||||
if (ret == EFI_SUCCESS)
|
||||
efi_st_error("Uninstall failed\n");
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(controllers) = {
|
||||
.name = "controllers",
|
||||
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup,
|
||||
.execute = execute,
|
||||
};
|
||||
@@ -0,0 +1,142 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_crc32
|
||||
*
|
||||
* Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* This unit test checks the CalculateCrc32 bootservice and checks the
|
||||
* headers of the system table, the boot services table, and the runtime
|
||||
* services table before and after ExitBootServices().
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
#include <u-boot/crc.h>
|
||||
|
||||
const struct efi_system_table *st;
|
||||
efi_status_t (EFIAPI *bs_crc32)(const void *data, efi_uintn_t data_size,
|
||||
u32 *crc32);
|
||||
|
||||
static int check_table(const void *table)
|
||||
{
|
||||
efi_status_t ret;
|
||||
u32 crc32, res;
|
||||
/* Casting from constant to not constant */
|
||||
struct efi_table_hdr *hdr = (struct efi_table_hdr *)table;
|
||||
|
||||
if (!hdr->signature) {
|
||||
efi_st_error("Missing header signature\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (!hdr->revision) {
|
||||
efi_st_error("Missing header revision\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (hdr->headersize <= sizeof(struct efi_table_hdr)) {
|
||||
efi_st_error("Incorrect headersize value\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (hdr->reserved) {
|
||||
efi_st_error("Reserved header field is not zero\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
crc32 = hdr->crc32;
|
||||
/*
|
||||
* Setting the crc32 of the 'const' table to zero is easier than
|
||||
* copying
|
||||
*/
|
||||
hdr->crc32 = 0;
|
||||
ret = bs_crc32(table, hdr->headersize, &res);
|
||||
/* Reset table crc32 so it stays constant */
|
||||
hdr->crc32 = crc32;
|
||||
if (ret != EFI_ST_SUCCESS) {
|
||||
efi_st_error("CalculateCrc32 failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (res != crc32) {
|
||||
efi_st_error("Incorrect CRC32\n");
|
||||
// return EFI_ST_FAILURE;
|
||||
}
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup unit test.
|
||||
*
|
||||
* Check that CalculateCrc32 is working correctly.
|
||||
* Check tables before ExitBootServices().
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int setup(const efi_handle_t handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
efi_status_t ret;
|
||||
u32 res;
|
||||
|
||||
st = systable;
|
||||
bs_crc32 = systable->boottime->calculate_crc32;
|
||||
|
||||
/* Check that CalculateCrc32 is working */
|
||||
ret = bs_crc32("U-Boot", 6, &res);
|
||||
if (ret != EFI_ST_SUCCESS) {
|
||||
efi_st_error("CalculateCrc32 failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (res != 0x134b0db4) {
|
||||
efi_st_error("Incorrect CRC32\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/* Check tables before ExitBootServices() */
|
||||
if (check_table(st) != EFI_ST_SUCCESS) {
|
||||
efi_st_error("Checking system table\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (check_table(st->boottime) != EFI_ST_SUCCESS) {
|
||||
efi_st_error("Checking boottime table\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (check_table(st->runtime) != EFI_ST_SUCCESS) {
|
||||
efi_st_error("Checking runtime table\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute unit test
|
||||
*
|
||||
* Check tables after ExitBootServices()
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
if (check_table(st) != EFI_ST_SUCCESS) {
|
||||
efi_st_error("Checking system table\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (check_table(st->runtime) != EFI_ST_SUCCESS) {
|
||||
efi_st_error("Checking runtime table\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/*
|
||||
* We cannot call SetVirtualAddressMap() and recheck the runtime
|
||||
* table afterwards because this would invalidate the addresses of the
|
||||
* unit tests.
|
||||
*/
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(crc32) = {
|
||||
.name = "crc32",
|
||||
.phase = EFI_SETUP_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup,
|
||||
.execute = execute,
|
||||
};
|
||||
@@ -0,0 +1,402 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_devicepath
|
||||
*
|
||||
* Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* This unit test checks the following protocol services:
|
||||
* DevicePathToText
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
|
||||
static struct efi_boot_services *boottime;
|
||||
|
||||
static efi_handle_t handle1;
|
||||
static efi_handle_t handle2;
|
||||
static efi_handle_t handle3;
|
||||
|
||||
struct interface {
|
||||
void (EFIAPI * inc)(void);
|
||||
} interface;
|
||||
|
||||
static efi_guid_t guid_device_path = EFI_DEVICE_PATH_PROTOCOL_GUID;
|
||||
|
||||
static efi_guid_t guid_device_path_to_text_protocol =
|
||||
EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID;
|
||||
|
||||
static efi_guid_t guid_protocol =
|
||||
EFI_GUID(0xdbca4c98, 0x6cb0, 0x694d,
|
||||
0x08, 0x72, 0x81, 0x9c, 0x65, 0x0c, 0xbb, 0x7d);
|
||||
|
||||
static efi_guid_t guid_vendor1 =
|
||||
EFI_GUID(0xdbca4c98, 0x6cb0, 0x694d,
|
||||
0x08, 0x72, 0x81, 0x9c, 0x65, 0x0c, 0xbb, 0xb1);
|
||||
|
||||
static efi_guid_t guid_vendor2 =
|
||||
EFI_GUID(0xdbca4c98, 0x6cb0, 0x694d,
|
||||
0x08, 0x72, 0x81, 0x9c, 0x65, 0x0c, 0xbb, 0xa2);
|
||||
|
||||
static efi_guid_t guid_vendor3 =
|
||||
EFI_GUID(0xdbca4c98, 0x6cb0, 0x694d,
|
||||
0x08, 0x72, 0x81, 0x9c, 0x65, 0x0c, 0xbb, 0xc3);
|
||||
|
||||
static u8 *dp1;
|
||||
static u8 *dp2;
|
||||
static u8 *dp3;
|
||||
|
||||
struct efi_device_path_to_text_protocol *device_path_to_text;
|
||||
|
||||
/*
|
||||
* Setup unit test.
|
||||
*
|
||||
* Create three handles. Install a new protocol on two of them and
|
||||
* provide device paths.
|
||||
*
|
||||
* handle1
|
||||
* guid interface
|
||||
* handle2
|
||||
* guid interface
|
||||
* handle3
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
*/
|
||||
static int setup(const efi_handle_t img_handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
struct efi_device_path_vendor vendor_node;
|
||||
struct efi_device_path end_node;
|
||||
efi_status_t ret;
|
||||
|
||||
boottime = systable->boottime;
|
||||
|
||||
ret = boottime->locate_protocol(&guid_device_path_to_text_protocol,
|
||||
NULL, (void **)&device_path_to_text);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
device_path_to_text = NULL;
|
||||
efi_st_error(
|
||||
"Device path to text protocol is not available.\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
ret = boottime->allocate_pool(EFI_LOADER_DATA,
|
||||
sizeof(struct efi_device_path_vendor) +
|
||||
sizeof(struct efi_device_path),
|
||||
(void **)&dp1);
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out_of_memory;
|
||||
|
||||
ret = boottime->allocate_pool(EFI_LOADER_DATA, 2 *
|
||||
sizeof(struct efi_device_path_vendor) +
|
||||
sizeof(struct efi_device_path),
|
||||
(void **)&dp2);
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out_of_memory;
|
||||
|
||||
ret = boottime->allocate_pool(EFI_LOADER_DATA, 3 *
|
||||
sizeof(struct efi_device_path_vendor) +
|
||||
sizeof(struct efi_device_path),
|
||||
(void **)&dp3);
|
||||
if (ret != EFI_SUCCESS)
|
||||
goto out_of_memory;
|
||||
|
||||
vendor_node.dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE;
|
||||
vendor_node.dp.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR;
|
||||
vendor_node.dp.length = sizeof(struct efi_device_path_vendor);
|
||||
|
||||
boottime->copy_mem(&vendor_node.guid, &guid_vendor1,
|
||||
sizeof(efi_guid_t));
|
||||
boottime->copy_mem(dp1, &vendor_node,
|
||||
sizeof(struct efi_device_path_vendor));
|
||||
boottime->copy_mem(dp2, &vendor_node,
|
||||
sizeof(struct efi_device_path_vendor));
|
||||
boottime->copy_mem(dp3, &vendor_node,
|
||||
sizeof(struct efi_device_path_vendor));
|
||||
|
||||
boottime->copy_mem(&vendor_node.guid, &guid_vendor2,
|
||||
sizeof(efi_guid_t));
|
||||
boottime->copy_mem(dp2 + sizeof(struct efi_device_path_vendor),
|
||||
&vendor_node, sizeof(struct efi_device_path_vendor));
|
||||
boottime->copy_mem(dp3 + sizeof(struct efi_device_path_vendor),
|
||||
&vendor_node, sizeof(struct efi_device_path_vendor));
|
||||
|
||||
boottime->copy_mem(&vendor_node.guid, &guid_vendor3,
|
||||
sizeof(efi_guid_t));
|
||||
boottime->copy_mem(dp3 + 2 * sizeof(struct efi_device_path_vendor),
|
||||
&vendor_node, sizeof(struct efi_device_path_vendor));
|
||||
|
||||
end_node.type = DEVICE_PATH_TYPE_END;
|
||||
end_node.sub_type = DEVICE_PATH_SUB_TYPE_END;
|
||||
end_node.length = sizeof(struct efi_device_path);
|
||||
boottime->copy_mem(dp1 + sizeof(struct efi_device_path_vendor),
|
||||
&end_node, sizeof(struct efi_device_path));
|
||||
boottime->copy_mem(dp2 + 2 * sizeof(struct efi_device_path_vendor),
|
||||
&end_node, sizeof(struct efi_device_path));
|
||||
boottime->copy_mem(dp3 + 3 * sizeof(struct efi_device_path_vendor),
|
||||
&end_node, sizeof(struct efi_device_path));
|
||||
|
||||
ret = boottime->install_protocol_interface(&handle1,
|
||||
&guid_device_path,
|
||||
EFI_NATIVE_INTERFACE,
|
||||
dp1);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("InstallProtocolInterface failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->install_protocol_interface(&handle1,
|
||||
&guid_protocol,
|
||||
EFI_NATIVE_INTERFACE,
|
||||
&interface);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("InstallProtocolInterface failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->install_protocol_interface(&handle2,
|
||||
&guid_device_path,
|
||||
EFI_NATIVE_INTERFACE,
|
||||
dp2);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("InstallProtocolInterface failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->install_protocol_interface(&handle2,
|
||||
&guid_protocol,
|
||||
EFI_NATIVE_INTERFACE,
|
||||
&interface);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("InstallProtocolInterface failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->install_protocol_interface(&handle3,
|
||||
&guid_device_path,
|
||||
EFI_NATIVE_INTERFACE,
|
||||
dp3);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("InstallProtocolInterface failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
return EFI_ST_SUCCESS;
|
||||
|
||||
out_of_memory:
|
||||
efi_st_error("Out of memory\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tear down unit test.
|
||||
*
|
||||
*/
|
||||
static int teardown(void)
|
||||
{
|
||||
efi_status_t ret;
|
||||
|
||||
ret = boottime->uninstall_protocol_interface(handle1,
|
||||
&guid_device_path,
|
||||
dp1);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("UninstallProtocolInterface failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->uninstall_protocol_interface(handle1,
|
||||
&guid_protocol,
|
||||
&interface);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("UninstallProtocolInterface failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->uninstall_protocol_interface(handle2,
|
||||
&guid_device_path,
|
||||
dp2);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("UninstallProtocolInterface failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->uninstall_protocol_interface(handle2,
|
||||
&guid_protocol,
|
||||
&interface);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("UninstallProtocolInterface failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->uninstall_protocol_interface(handle3,
|
||||
&guid_device_path,
|
||||
dp3);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("UninstallProtocolInterface failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (dp1) {
|
||||
ret = boottime->free_pool(dp1);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("FreePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
}
|
||||
if (dp2) {
|
||||
ret = boottime->free_pool(dp2);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("FreePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
}
|
||||
if (dp3) {
|
||||
ret = boottime->free_pool(dp3);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("FreePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
}
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute unit test.
|
||||
*
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
struct efi_device_path *remaining_dp;
|
||||
efi_handle_t handle;
|
||||
/*
|
||||
* This device path node ends with the letter 't' of 'u-boot'.
|
||||
* The following '.bin' does not belong to the node but is
|
||||
* helps to test the correct truncation.
|
||||
*/
|
||||
struct {
|
||||
struct efi_device_path dp;
|
||||
u16 text[12];
|
||||
} __packed dp_node = {
|
||||
{ DEVICE_PATH_TYPE_MEDIA_DEVICE,
|
||||
DEVICE_PATH_SUB_TYPE_FILE_PATH,
|
||||
sizeof(struct efi_device_path) + 12},
|
||||
L"u-boot.bin",
|
||||
};
|
||||
u16 *string;
|
||||
efi_status_t ret;
|
||||
efi_uintn_t i, no_handles;
|
||||
efi_handle_t *handles;
|
||||
struct efi_device_path *dp;
|
||||
|
||||
/* Display all available device paths */
|
||||
ret = boottime->locate_handle_buffer(BY_PROTOCOL,
|
||||
&guid_device_path,
|
||||
NULL, &no_handles, &handles);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Cannot retrieve device path protocols.\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
efi_st_printf("Installed device path protocols:\n");
|
||||
for (i = 0; i < no_handles; ++i) {
|
||||
ret = boottime->open_protocol(handles[i], &guid_device_path,
|
||||
(void **)&dp, NULL, NULL,
|
||||
EFI_OPEN_PROTOCOL_GET_PROTOCOL);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Cannot open device path protocol.\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
string = device_path_to_text->convert_device_path_to_text(
|
||||
dp, true, false);
|
||||
if (!string) {
|
||||
efi_st_error("ConvertDevicePathToText failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
efi_st_printf("%ps\n", string);
|
||||
ret = boottime->free_pool(string);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("FreePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/*
|
||||
* CloseProtocol cannot be called without agent handle.
|
||||
* There is no need to close the device path protocol.
|
||||
*/
|
||||
}
|
||||
ret = boottime->free_pool(handles);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("FreePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/* Test ConvertDevicePathToText */
|
||||
string = device_path_to_text->convert_device_path_to_text(
|
||||
(struct efi_device_path *)dp2, true, false);
|
||||
if (!string) {
|
||||
efi_st_error("ConvertDevicePathToText failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (efi_st_strcmp_16_8(
|
||||
string,
|
||||
"/VenHw(dbca4c98-6cb0-694d-0872-819c650cbbb1)/VenHw(dbca4c98-6cb0-694d-0872-819c650cbba2)")
|
||||
) {
|
||||
efi_st_printf("dp2: %ps\n", string);
|
||||
efi_st_error("Incorrect text from ConvertDevicePathToText\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->free_pool(string);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("FreePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/* Test ConvertDeviceNodeToText */
|
||||
string = device_path_to_text->convert_device_node_to_text(
|
||||
(struct efi_device_path *)&dp_node, true, false);
|
||||
if (!string) {
|
||||
efi_st_error("ConvertDeviceNodeToText failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (efi_st_strcmp_16_8(string, "u-boot")) {
|
||||
efi_st_printf("dp_node: %ps\n", string);
|
||||
efi_st_error(
|
||||
"Incorrect conversion by ConvertDeviceNodeToText\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->free_pool(string);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("FreePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/* Test LocateDevicePath */
|
||||
remaining_dp = (struct efi_device_path *)dp3;
|
||||
ret = boottime->locate_device_path(&guid_protocol, &remaining_dp,
|
||||
&handle);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("LocateDevicePath failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (handle != handle2) {
|
||||
efi_st_error("LocateDevicePath returned wrong handle\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
string = device_path_to_text->convert_device_path_to_text(remaining_dp,
|
||||
true, false);
|
||||
if (!string) {
|
||||
efi_st_error("ConvertDevicePathToText failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (efi_st_strcmp_16_8(string,
|
||||
"/VenHw(dbca4c98-6cb0-694d-0872-819c650cbbc3)")
|
||||
) {
|
||||
efi_st_printf("remaining device path: %ps\n", string);
|
||||
efi_st_error("LocateDevicePath: wrong remaining device path\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->free_pool(string);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("FreePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(devicepath) = {
|
||||
.name = "device path",
|
||||
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup,
|
||||
.execute = execute,
|
||||
.teardown = teardown,
|
||||
};
|
||||
@@ -0,0 +1,280 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_devicepath_util
|
||||
*
|
||||
* Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* This unit test checks the device path utilities protocol.
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
|
||||
static struct efi_boot_services *boottime;
|
||||
|
||||
static efi_guid_t guid_device_path_utilities_protocol =
|
||||
EFI_DEVICE_PATH_UTILITIES_PROTOCOL_GUID;
|
||||
|
||||
struct efi_device_path_utilities_protocol *dpu;
|
||||
|
||||
/*
|
||||
* Setup unit test.
|
||||
*
|
||||
* Locate the device path utilities protocol.
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
*/
|
||||
static int setup(const efi_handle_t img_handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
int ret;
|
||||
|
||||
boottime = systable->boottime;
|
||||
|
||||
ret = boottime->locate_protocol(&guid_device_path_utilities_protocol,
|
||||
NULL, (void **)&dpu);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
dpu = NULL;
|
||||
efi_st_error(
|
||||
"Device path to text protocol is not available.\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a device path consisting of a single media device node followed by an
|
||||
* end node.
|
||||
*
|
||||
* @length: length of the media device node
|
||||
* @dp: device path
|
||||
* @return: status code
|
||||
*/
|
||||
static int create_single_node_device_path(unsigned int length,
|
||||
struct efi_device_path **dp)
|
||||
{
|
||||
struct efi_device_path *node;
|
||||
efi_uintn_t len;
|
||||
int ret;
|
||||
|
||||
node = dpu->create_device_node(DEVICE_PATH_TYPE_MEDIA_DEVICE,
|
||||
DEVICE_PATH_SUB_TYPE_FILE_PATH, length);
|
||||
if (!node) {
|
||||
efi_st_error("CreateDeviceNode failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
*dp = dpu->append_device_node(NULL, node);
|
||||
if (!*dp) {
|
||||
efi_st_error("AppendDeviceNode failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->free_pool(node);
|
||||
if (ret != EFI_ST_SUCCESS) {
|
||||
efi_st_error("FreePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
len = dpu->get_device_path_size(*dp);
|
||||
if (len != length + 4) {
|
||||
efi_st_error("Wrong device path length %u, expected %u\n",
|
||||
(unsigned int)len, length);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute unit test.
|
||||
*
|
||||
* In the test device paths are created, copied, and concatenated. The device
|
||||
* path length is used as a measure of success.
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
struct efi_device_path *dp1;
|
||||
struct efi_device_path *dp2;
|
||||
struct efi_device_path *dp3;
|
||||
|
||||
efi_uintn_t len;
|
||||
int ret;
|
||||
|
||||
/* IsDevicePathMultiInstance(NULL) */
|
||||
if (dpu->is_device_path_multi_instance(NULL)) {
|
||||
efi_st_error("IsDevicePathMultiInstance(NULL) returned true\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* GetDevicePathSize(NULL) */
|
||||
len = dpu->get_device_path_size(NULL);
|
||||
if (len) {
|
||||
efi_st_error("Wrong device path length %u, expected 0\n",
|
||||
(unsigned int)len);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* DuplicateDevicePath(NULL) */
|
||||
dp1 = dpu->duplicate_device_path(NULL);
|
||||
if (dp1) {
|
||||
efi_st_error("DuplicateDevicePath(NULL) failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* AppendDevicePath(NULL, NULL) */
|
||||
dp1 = dpu->append_device_path(NULL, NULL);
|
||||
if (!dp1) {
|
||||
efi_st_error("AppendDevicePath(NULL, NULL) failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
len = dpu->get_device_path_size(dp1);
|
||||
if (len != 4) {
|
||||
efi_st_error("Wrong device path length %u, expected 4\n",
|
||||
(unsigned int)len);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->free_pool(dp1);
|
||||
if (ret != EFI_ST_SUCCESS) {
|
||||
efi_st_error("FreePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* CreateDeviceNode */
|
||||
ret = create_single_node_device_path(21, &dp1);
|
||||
if (ret != EFI_ST_SUCCESS)
|
||||
return ret;
|
||||
ret = create_single_node_device_path(17, &dp2);
|
||||
if (ret != EFI_ST_SUCCESS)
|
||||
return ret;
|
||||
/* AppendDevicePath */
|
||||
dp3 = dpu->append_device_path(dp1, dp2);
|
||||
if (!dp3) {
|
||||
efi_st_error("AppendDevicePath failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (dp3 == dp1 || dp3 == dp2) {
|
||||
efi_st_error("AppendDevicePath reused buffer\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
len = dpu->get_device_path_size(dp3);
|
||||
/* 21 + 17 + 4 */
|
||||
if (len != 42) {
|
||||
efi_st_error("Wrong device path length %u, expected 42\n",
|
||||
(unsigned int)len);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->free_pool(dp2);
|
||||
if (ret != EFI_ST_SUCCESS) {
|
||||
efi_st_error("FreePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* AppendDeviceNode */
|
||||
dp2 = dpu->append_device_node(dp1, dp3);
|
||||
if (!dp2) {
|
||||
efi_st_error("AppendDevicePath failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
len = dpu->get_device_path_size(dp2);
|
||||
/* 21 + 21 + 4 */
|
||||
if (len != 46) {
|
||||
printf("%s(%d) %s\n", __FILE__, __LINE__, __func__);
|
||||
efi_st_error("Wrong device path length %u, expected 46\n",
|
||||
(unsigned int)len);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->free_pool(dp1);
|
||||
if (ret != EFI_ST_SUCCESS) {
|
||||
efi_st_error("FreePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* IsDevicePathMultiInstance */
|
||||
if (dpu->is_device_path_multi_instance(dp2)) {
|
||||
printf("%s(%d) %s\n", __FILE__, __LINE__, __func__);
|
||||
efi_st_error("IsDevicePathMultiInstance returned true\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* AppendDevicePathInstance */
|
||||
dp1 = dpu->append_device_path_instance(dp2, dp3);
|
||||
if (!dp1) {
|
||||
efi_st_error("AppendDevicePathInstance failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
len = dpu->get_device_path_size(dp1);
|
||||
/* 46 + 42 */
|
||||
if (len != 88) {
|
||||
efi_st_error("Wrong device path length %u, expected 88\n",
|
||||
(unsigned int)len);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* IsDevicePathMultiInstance */
|
||||
if (!dpu->is_device_path_multi_instance(dp1)) {
|
||||
efi_st_error("IsDevicePathMultiInstance returned false\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->free_pool(dp2);
|
||||
if (ret != EFI_ST_SUCCESS) {
|
||||
efi_st_error("FreePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->free_pool(dp3);
|
||||
if (ret != EFI_ST_SUCCESS) {
|
||||
efi_st_error("FreePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* GetNextDevicePathInstance */
|
||||
dp3 = dp1;
|
||||
dp2 = dpu->get_next_device_path_instance(&dp1, &len);
|
||||
if (!dp2) {
|
||||
efi_st_error("GetNextDevicePathInstance failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (!dp1) {
|
||||
efi_st_error("GetNextDevicePathInstance no 2nd instance\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (len != 46) {
|
||||
efi_st_error("Wrong device path length %u, expected 46\n",
|
||||
(unsigned int)len);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
len = dpu->get_device_path_size(dp1);
|
||||
if (len != 42) {
|
||||
efi_st_error("Wrong device path length %u, expected 42\n",
|
||||
(unsigned int)len);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->free_pool(dp2);
|
||||
if (ret != EFI_ST_SUCCESS) {
|
||||
efi_st_error("FreePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
dp2 = dpu->get_next_device_path_instance(&dp1, &len);
|
||||
if (!dp2) {
|
||||
efi_st_error("GetNextDevicePathInstance failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (len != 42) {
|
||||
efi_st_error("Wrong device path length %u, expected 46\n",
|
||||
(unsigned int)len);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (dp1) {
|
||||
efi_st_error("GetNextDevicePathInstance did not signal end\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/* Clean up */
|
||||
ret = boottime->free_pool(dp2);
|
||||
if (ret != EFI_ST_SUCCESS) {
|
||||
efi_st_error("FreePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->free_pool(dp3);
|
||||
if (ret != EFI_ST_SUCCESS) {
|
||||
efi_st_error("FreePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(dputil) = {
|
||||
.name = "device path utilities protocol",
|
||||
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup,
|
||||
.execute = execute,
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Non-zero 8 byte strings of a disk image
|
||||
*
|
||||
* Generated with tools/file2include
|
||||
*/
|
||||
|
||||
#define EFI_ST_DISK_IMG { 0x00010000, { \
|
||||
{0x000001b8, "\x21\x5d\x53\xd1\x00\x00\x00\x00"}, /* !]S..... */ \
|
||||
{0x000001c0, "\x02\x00\x01\x02\x02\x00\x01\x00"}, /* ........ */ \
|
||||
{0x000001c8, "\x00\x00\x7f\x00\x00\x00\x00\x00"}, /* ........ */ \
|
||||
{0x000001f8, "\x00\x00\x00\x00\x00\x00\x55\xaa"}, /* ......U. */ \
|
||||
{0x00000200, "\xeb\x3c\x90\x6d\x6b\x66\x73\x2e"}, /* .<.mkfs. */ \
|
||||
{0x00000208, "\x66\x61\x74\x00\x02\x04\x01\x00"}, /* fat..... */ \
|
||||
{0x00000210, "\x02\x00\x02\x7f\x00\xf8\x01\x00"}, /* ........ */ \
|
||||
{0x00000218, "\x20\x00\x40\x00\x00\x00\x00\x00"}, /* .@..... */ \
|
||||
{0x00000220, "\x00\x00\x00\x00\x80\x00\x29\xc4"}, /* ......). */ \
|
||||
{0x00000228, "\xc4\x88\x11\x55\x2d\x42\x4f\x4f"}, /* ...U-BOO */ \
|
||||
{0x00000230, "\x54\x20\x54\x45\x53\x54\x46\x41"}, /* T TESTFA */ \
|
||||
{0x00000238, "\x54\x31\x32\x20\x20\x20\x0e\x1f"}, /* T12 .. */ \
|
||||
{0x00000240, "\xbe\x5b\x7c\xac\x22\xc0\x74\x0b"}, /* .[|.".t. */ \
|
||||
{0x00000248, "\x56\xb4\x0e\xbb\x07\x00\xcd\x10"}, /* V....... */ \
|
||||
{0x00000250, "\x5e\xeb\xf0\x32\xe4\xcd\x16\xcd"}, /* ^..2.... */ \
|
||||
{0x00000258, "\x19\xeb\xfe\x54\x68\x69\x73\x20"}, /* ...This */ \
|
||||
{0x00000260, "\x69\x73\x20\x6e\x6f\x74\x20\x61"}, /* is not a */ \
|
||||
{0x00000268, "\x20\x62\x6f\x6f\x74\x61\x62\x6c"}, /* bootabl */ \
|
||||
{0x00000270, "\x65\x20\x64\x69\x73\x6b\x2e\x20"}, /* e disk. */ \
|
||||
{0x00000278, "\x20\x50\x6c\x65\x61\x73\x65\x20"}, /* Please */ \
|
||||
{0x00000280, "\x69\x6e\x73\x65\x72\x74\x20\x61"}, /* insert a */ \
|
||||
{0x00000288, "\x20\x62\x6f\x6f\x74\x61\x62\x6c"}, /* bootabl */ \
|
||||
{0x00000290, "\x65\x20\x66\x6c\x6f\x70\x70\x79"}, /* e floppy */ \
|
||||
{0x00000298, "\x20\x61\x6e\x64\x0d\x0a\x70\x72"}, /* and..pr */ \
|
||||
{0x000002a0, "\x65\x73\x73\x20\x61\x6e\x79\x20"}, /* ess any */ \
|
||||
{0x000002a8, "\x6b\x65\x79\x20\x74\x6f\x20\x74"}, /* key to t */ \
|
||||
{0x000002b0, "\x72\x79\x20\x61\x67\x61\x69\x6e"}, /* ry again */ \
|
||||
{0x000002b8, "\x20\x2e\x2e\x2e\x20\x0d\x0a\x00"}, /* ... ... */ \
|
||||
{0x000003f8, "\x00\x00\x00\x00\x00\x00\x55\xaa"}, /* ......U. */ \
|
||||
{0x00000400, "\xf8\xff\xff\x00\xf0\xff\x00\x00"}, /* ........ */ \
|
||||
{0x00000600, "\xf8\xff\xff\x00\xf0\xff\x00\x00"}, /* ........ */ \
|
||||
{0x00000800, "\x55\x2d\x42\x4f\x4f\x54\x20\x54"}, /* U-BOOT T */ \
|
||||
{0x00000808, "\x45\x53\x54\x08\x00\x00\xaa\x56"}, /* EST....V */ \
|
||||
{0x00000810, "\x84\x4c\x84\x4c\x00\x00\xaa\x56"}, /* .L.L...V */ \
|
||||
{0x00000818, "\x84\x4c\x00\x00\x00\x00\x00\x00"}, /* .L...... */ \
|
||||
{0x00000820, "\x41\x68\x00\x65\x00\x6c\x00\x6c"}, /* Ah.e.l.l */ \
|
||||
{0x00000828, "\x00\x6f\x00\x0f\x00\xf1\x2e\x00"}, /* .o...... */ \
|
||||
{0x00000830, "\x74\x00\x78\x00\x74\x00\x00\x00"}, /* t.x.t... */ \
|
||||
{0x00000838, "\xff\xff\x00\x00\xff\xff\xff\xff"}, /* ........ */ \
|
||||
{0x00000840, "\x48\x45\x4c\x4c\x4f\x20\x20\x20"}, /* HELLO */ \
|
||||
{0x00000848, "\x54\x58\x54\x20\x00\x64\xd7\x46"}, /* TXT .d.F */ \
|
||||
{0x00000850, "\x84\x4c\x84\x4c\x00\x00\xd7\x46"}, /* .L.L...F */ \
|
||||
{0x00000858, "\x84\x4c\x03\x00\x0d\x00\x00\x00"}, /* .L...... */ \
|
||||
{0x00005000, "\x48\x65\x6c\x6c\x6f\x20\x77\x6f"}, /* Hello wo */ \
|
||||
{0x00005008, "\x72\x6c\x64\x21\x0a\x00\x00\x00"}, /* rld!.... */ \
|
||||
{0, NULL} } }
|
||||
@@ -0,0 +1,138 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_event_groups
|
||||
*
|
||||
* Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* This test checks the notification of group events and the
|
||||
* following services:
|
||||
* CreateEventEx, CloseEvent, SignalEvent, CheckEvent.
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
|
||||
#define GROUP_SIZE 16
|
||||
|
||||
static struct efi_boot_services *boottime;
|
||||
static efi_guid_t event_group =
|
||||
EFI_GUID(0x2335905b, 0xc3b9, 0x4221, 0xa3, 0x71,
|
||||
0x0e, 0x5b, 0x45, 0xc0, 0x56, 0x91);
|
||||
|
||||
/*
|
||||
* Notification function, increments the notification count if parameter
|
||||
* context is provided.
|
||||
*
|
||||
* @event notified event
|
||||
* @context pointer to the notification count
|
||||
*/
|
||||
static void EFIAPI notify(struct efi_event *event, void *context)
|
||||
{
|
||||
unsigned int *count = context;
|
||||
|
||||
if (count)
|
||||
++*count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup unit test.
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int setup(const efi_handle_t handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
boottime = systable->boottime;
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute unit test.
|
||||
*
|
||||
* Create multiple events in an event group. Signal each event once and check
|
||||
* that all events are notified once in each round.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
unsigned int counter[GROUP_SIZE] = {0};
|
||||
struct efi_event *events[GROUP_SIZE];
|
||||
size_t i, j;
|
||||
efi_status_t ret;
|
||||
|
||||
for (i = 0; i < GROUP_SIZE; ++i) {
|
||||
ret = boottime->create_event_ex(0, TPL_NOTIFY,
|
||||
notify, (void *)&counter[i],
|
||||
&event_group, &events[i]);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to create event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < GROUP_SIZE; ++i) {
|
||||
ret = boottime->signal_event(events[i]);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to signal event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
for (j = 0; j < GROUP_SIZE; ++j) {
|
||||
if (counter[j] != 2 * i + 1) {
|
||||
efi_st_printf("i %u, j %u, count %u\n",
|
||||
(unsigned int)i, (unsigned int)j,
|
||||
(unsigned int)counter[j]);
|
||||
efi_st_error("Notification function was not called\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Clear signaled state */
|
||||
ret = boottime->check_event(events[j]);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Event was not signaled\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (counter[j] != 2 * i + 1) {
|
||||
efi_st_printf("i %u, j %u, count %u\n",
|
||||
(unsigned int)i, (unsigned int)j,
|
||||
(unsigned int)counter[j]);
|
||||
efi_st_error(
|
||||
"Notification function was called\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Call notification function */
|
||||
ret = boottime->check_event(events[j]);
|
||||
if (ret != EFI_NOT_READY) {
|
||||
efi_st_error(
|
||||
"Signaled state not cleared\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (counter[j] != 2 * i + 2) {
|
||||
efi_st_printf("i %u, j %u, count %u\n",
|
||||
(unsigned int)i, (unsigned int)j,
|
||||
(unsigned int)counter[j]);
|
||||
efi_st_error(
|
||||
"Notification function not called\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < GROUP_SIZE; ++i) {
|
||||
ret = boottime->close_event(events[i]);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to close event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(eventgoups) = {
|
||||
.name = "event groups",
|
||||
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup,
|
||||
.execute = execute,
|
||||
};
|
||||
@@ -0,0 +1,204 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_events
|
||||
*
|
||||
* Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* This unit test uses timer events to check the implementation
|
||||
* of the following boottime services:
|
||||
* CreateEvent, CloseEvent, WaitForEvent, CheckEvent, SetTimer.
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
|
||||
static struct efi_event *event_notify;
|
||||
static struct efi_event *event_wait;
|
||||
static unsigned int timer_ticks;
|
||||
static struct efi_boot_services *boottime;
|
||||
|
||||
/*
|
||||
* Notification function, increments the notification count if parameter
|
||||
* context is provided.
|
||||
*
|
||||
* @event notified event
|
||||
* @context pointer to the notification count
|
||||
*/
|
||||
static void EFIAPI notify(struct efi_event *event, void *context)
|
||||
{
|
||||
unsigned int *count = context;
|
||||
|
||||
if (count)
|
||||
++*count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup unit test.
|
||||
*
|
||||
* Create two timer events.
|
||||
* One with EVT_NOTIFY_SIGNAL, the other with EVT_NOTIFY_WAIT.
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int setup(const efi_handle_t handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
efi_status_t ret;
|
||||
|
||||
boottime = systable->boottime;
|
||||
|
||||
ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL,
|
||||
TPL_CALLBACK, notify, (void *)&timer_ticks,
|
||||
&event_notify);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("could not create event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_WAIT,
|
||||
TPL_CALLBACK, notify, NULL, &event_wait);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("could not create event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tear down unit test.
|
||||
*
|
||||
* Close the events created in setup.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int teardown(void)
|
||||
{
|
||||
efi_status_t ret;
|
||||
|
||||
if (event_notify) {
|
||||
ret = boottime->close_event(event_notify);
|
||||
event_notify = NULL;
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("could not close event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
}
|
||||
if (event_wait) {
|
||||
ret = boottime->close_event(event_wait);
|
||||
event_wait = NULL;
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("could not close event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
}
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute unit test.
|
||||
*
|
||||
* Run a 10 ms periodic timer and check that it is called 10 times
|
||||
* while waiting for 100 ms single shot timer.
|
||||
*
|
||||
* Run a 100 ms single shot timer and check that it is called once
|
||||
* while waiting for 100 ms periodic timer for two periods.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
efi_uintn_t index;
|
||||
efi_status_t ret;
|
||||
|
||||
/* Set 10 ms timer */
|
||||
timer_ticks = 0;
|
||||
ret = boottime->set_timer(event_notify, EFI_TIMER_PERIODIC, 100000);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Could not set timer\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Set 100 ms timer */
|
||||
ret = boottime->set_timer(event_wait, EFI_TIMER_RELATIVE, 1000000);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Could not set timer\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/* Set some arbitrary non-zero value to make change detectable. */
|
||||
index = 5;
|
||||
ret = boottime->wait_for_event(1, &event_wait, &index);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Could not wait for event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->check_event(event_wait);
|
||||
if (ret != EFI_NOT_READY) {
|
||||
efi_st_error("Signaled state was not cleared.\n");
|
||||
efi_st_printf("ret = %u\n", (unsigned int)ret);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (index != 0) {
|
||||
efi_st_error("WaitForEvent returned wrong index\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (timer_ticks < 8 || timer_ticks > 12) {
|
||||
efi_st_printf("Notification count periodic: %u\n", timer_ticks);
|
||||
efi_st_error("Incorrect timing of events\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->set_timer(event_notify, EFI_TIMER_STOP, 0);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Could not cancel timer\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Set 10 ms timer */
|
||||
timer_ticks = 0;
|
||||
ret = boottime->set_timer(event_notify, EFI_TIMER_RELATIVE, 100000);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Could not set timer\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Set 100 ms timer */
|
||||
ret = boottime->set_timer(event_wait, EFI_TIMER_PERIODIC, 1000000);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Could not set timer\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->wait_for_event(1, &event_wait, &index);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Could not wait for event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (timer_ticks != 1) {
|
||||
efi_st_printf("Notification count single shot: %u\n",
|
||||
timer_ticks);
|
||||
efi_st_error("Single shot timer failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->wait_for_event(1, &event_wait, &index);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Could not wait for event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (timer_ticks != 1) {
|
||||
efi_st_printf("Notification count stopped timer: %u\n",
|
||||
timer_ticks);
|
||||
efi_st_error("Stopped timer fired\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->set_timer(event_wait, EFI_TIMER_STOP, 0);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Could not cancel timer\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(events) = {
|
||||
.name = "event services",
|
||||
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup,
|
||||
.execute = execute,
|
||||
.teardown = teardown,
|
||||
};
|
||||
@@ -0,0 +1,147 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_exception
|
||||
*
|
||||
* Copyright (c) 2019 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* This test checks the handling of exceptions.
|
||||
*
|
||||
* The efi_selftest_miniapp_exception.efi application is loaded into memory
|
||||
* and started.
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
/* Include containing the UEFI application */
|
||||
#include "efi_miniapp_file_image_exception.h"
|
||||
|
||||
/* Block size of compressed disk image */
|
||||
#define COMPRESSED_DISK_IMAGE_BLOCK_SIZE 8
|
||||
|
||||
/* Binary logarithm of the block size */
|
||||
#define LB_BLOCK_SIZE 9
|
||||
|
||||
/* File device path for LoadImage() */
|
||||
static struct {
|
||||
struct efi_device_path dp;
|
||||
u16 filename[8];
|
||||
struct efi_device_path end;
|
||||
} dp = {
|
||||
{
|
||||
DEVICE_PATH_TYPE_MEDIA_DEVICE,
|
||||
DEVICE_PATH_SUB_TYPE_FILE_PATH,
|
||||
sizeof(dp.dp) + sizeof(dp.filename),
|
||||
},
|
||||
L"bug.efi",
|
||||
{
|
||||
DEVICE_PATH_TYPE_END,
|
||||
DEVICE_PATH_SUB_TYPE_END,
|
||||
sizeof(dp.end),
|
||||
}
|
||||
};
|
||||
|
||||
static efi_handle_t image_handle;
|
||||
static struct efi_boot_services *boottime;
|
||||
|
||||
/* One 8 byte block of the compressed disk image */
|
||||
struct line {
|
||||
size_t addr;
|
||||
char *line;
|
||||
};
|
||||
|
||||
/* Compressed file image */
|
||||
struct compressed_file_image {
|
||||
size_t length;
|
||||
struct line lines[];
|
||||
};
|
||||
|
||||
static struct compressed_file_image img = EFI_ST_DISK_IMG;
|
||||
|
||||
/* Decompressed file image */
|
||||
static u8 *image;
|
||||
|
||||
/*
|
||||
* Decompress the disk image.
|
||||
*
|
||||
* @image decompressed disk image
|
||||
* @return status code
|
||||
*/
|
||||
static efi_status_t decompress(u8 **image)
|
||||
{
|
||||
u8 *buf;
|
||||
size_t i;
|
||||
size_t addr;
|
||||
size_t len;
|
||||
efi_status_t ret;
|
||||
|
||||
ret = boottime->allocate_pool(EFI_LOADER_DATA, img.length,
|
||||
(void **)&buf);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Out of memory\n");
|
||||
return ret;
|
||||
}
|
||||
boottime->set_mem(buf, img.length, 0);
|
||||
|
||||
for (i = 0; ; ++i) {
|
||||
if (!img.lines[i].line)
|
||||
break;
|
||||
addr = img.lines[i].addr;
|
||||
len = COMPRESSED_DISK_IMAGE_BLOCK_SIZE;
|
||||
if (addr + len > img.length)
|
||||
len = img.length - addr;
|
||||
boottime->copy_mem(buf + addr, img.lines[i].line, len);
|
||||
}
|
||||
*image = buf;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup unit test.
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int setup(const efi_handle_t handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
image_handle = handle;
|
||||
boottime = systable->boottime;
|
||||
|
||||
/* Load the application image into memory */
|
||||
decompress(&image);
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute unit test.
|
||||
*
|
||||
* Load and start the application image.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
efi_status_t ret;
|
||||
efi_handle_t handle;
|
||||
|
||||
ret = boottime->load_image(false, image_handle, &dp.dp, image,
|
||||
img.length, &handle);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to load image\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->start_image(handle, NULL, NULL);
|
||||
|
||||
efi_st_error("Exception not triggered\n");
|
||||
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(exception) = {
|
||||
.name = "exception",
|
||||
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup,
|
||||
.execute = execute,
|
||||
.on_request = true,
|
||||
};
|
||||
@@ -0,0 +1,88 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_exitbootservices
|
||||
*
|
||||
* Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* This unit test checks that the notification function of an
|
||||
* EVT_SIGNAL_EXIT_BOOT_SERVICES event is called exactly once.
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
|
||||
static struct efi_boot_services *boottime;
|
||||
static struct efi_event *event_notify;
|
||||
static unsigned int notification_count;
|
||||
|
||||
/*
|
||||
* Notification function, increments the notification count.
|
||||
*
|
||||
* @event notified event
|
||||
* @context pointer to the notification count
|
||||
*/
|
||||
static void EFIAPI notify(struct efi_event *event, void *context)
|
||||
{
|
||||
unsigned int *count = context;
|
||||
|
||||
++*count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup unit test.
|
||||
*
|
||||
* Create an EVT_SIGNAL_EXIT_BOOT_SERVICES event.
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int setup(const efi_handle_t handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
efi_status_t ret;
|
||||
|
||||
boottime = systable->boottime;
|
||||
|
||||
notification_count = 0;
|
||||
ret = boottime->create_event(EVT_SIGNAL_EXIT_BOOT_SERVICES,
|
||||
TPL_CALLBACK, notify,
|
||||
(void *)¬ification_count,
|
||||
&event_notify);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("could not create event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute unit test.
|
||||
*
|
||||
* Check that the notification function of the EVT_SIGNAL_EXIT_BOOT_SERVICES
|
||||
* event has been called.
|
||||
*
|
||||
* Call ExitBootServices again and check that the notification function is
|
||||
* not called again.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
if (notification_count != 1) {
|
||||
efi_st_error("ExitBootServices was not notified\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
efi_st_exit_boot_services();
|
||||
if (notification_count != 1) {
|
||||
efi_st_error("ExitBootServices was notified twice\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(exitbootservices) = {
|
||||
.name = "ExitBootServices",
|
||||
.phase = EFI_SETUP_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup,
|
||||
.execute = execute,
|
||||
};
|
||||
@@ -0,0 +1,206 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_pos
|
||||
*
|
||||
* Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* Test the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.
|
||||
*
|
||||
* The following services are tested:
|
||||
* OutputString, TestString, SetAttribute.
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
#include <linux/libfdt.h>
|
||||
|
||||
static const struct efi_system_table *systemtab;
|
||||
static const struct efi_boot_services *boottime;
|
||||
static const char *fdt;
|
||||
|
||||
/* This should be sufficient for */
|
||||
#define BUFFERSIZE 0x100000
|
||||
|
||||
static const efi_guid_t fdt_guid = EFI_FDT_GUID;
|
||||
static const efi_guid_t acpi_guid = EFI_ACPI_TABLE_GUID;
|
||||
|
||||
/*
|
||||
* Convert FDT value to host endianness.
|
||||
*
|
||||
* @val FDT value
|
||||
* @return converted value
|
||||
*/
|
||||
static uint32_t f2h(fdt32_t val)
|
||||
{
|
||||
char *buf = (char *)&val;
|
||||
char i;
|
||||
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
/* Swap the bytes */
|
||||
i = buf[0]; buf[0] = buf[3]; buf[3] = i;
|
||||
i = buf[1]; buf[1] = buf[2]; buf[2] = i;
|
||||
#endif
|
||||
return *(uint32_t *)buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the value of a property of the FDT root node.
|
||||
*
|
||||
* @name name of the property
|
||||
* @return value of the property
|
||||
*/
|
||||
static char *get_property(const u16 *property)
|
||||
{
|
||||
struct fdt_header *header = (struct fdt_header *)fdt;
|
||||
const fdt32_t *pos;
|
||||
const char *strings;
|
||||
|
||||
if (!header)
|
||||
return NULL;
|
||||
|
||||
if (f2h(header->magic) != FDT_MAGIC) {
|
||||
printf("Wrong magic\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pos = (fdt32_t *)(fdt + f2h(header->off_dt_struct));
|
||||
strings = fdt + f2h(header->off_dt_strings);
|
||||
|
||||
for (;;) {
|
||||
switch (f2h(pos[0])) {
|
||||
case FDT_BEGIN_NODE: {
|
||||
char *c = (char *)&pos[1];
|
||||
size_t i;
|
||||
|
||||
for (i = 0; c[i]; ++i)
|
||||
;
|
||||
pos = &pos[2 + (i >> 2)];
|
||||
break;
|
||||
}
|
||||
case FDT_PROP: {
|
||||
struct fdt_property *prop = (struct fdt_property *)pos;
|
||||
const char *label = &strings[f2h(prop->nameoff)];
|
||||
efi_status_t ret;
|
||||
|
||||
/* Check if this is the property to be returned */
|
||||
if (!efi_st_strcmp_16_8(property, label)) {
|
||||
char *str;
|
||||
efi_uintn_t len = f2h(prop->len);
|
||||
|
||||
if (!len)
|
||||
return NULL;
|
||||
/*
|
||||
* The string might not be 0 terminated.
|
||||
* It is safer to make a copy.
|
||||
*/
|
||||
ret = boottime->allocate_pool(
|
||||
EFI_LOADER_DATA, len + 1,
|
||||
(void **)&str);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_printf("AllocatePool failed\n");
|
||||
return NULL;
|
||||
}
|
||||
boottime->copy_mem(str, &pos[3], len);
|
||||
str[len] = 0;
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
pos = &pos[3 + ((f2h(prop->len) + 3) >> 2)];
|
||||
break;
|
||||
}
|
||||
case FDT_NOP:
|
||||
pos = &pos[1];
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_st_get_config_table() - get configuration table
|
||||
*
|
||||
* @guid: GUID of the configuration table
|
||||
* Return: pointer to configuration table or NULL
|
||||
*/
|
||||
static void *efi_st_get_config_table(const efi_guid_t *guid)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < systab.nr_tables; i++) {
|
||||
if (!guidcmp(guid, &systemtab->tables[i].guid))
|
||||
return systemtab->tables[i].table;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup unit test.
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int setup(const efi_handle_t img_handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
void *acpi;
|
||||
|
||||
systemtab = systable;
|
||||
boottime = systable->boottime;
|
||||
|
||||
acpi = efi_st_get_config_table(&acpi_guid);
|
||||
fdt = efi_st_get_config_table(&fdt_guid);
|
||||
|
||||
if (!fdt) {
|
||||
efi_st_error("Missing device tree\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (acpi) {
|
||||
efi_st_error("Found ACPI table and device tree\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute unit test.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
char *str;
|
||||
efi_status_t ret;
|
||||
|
||||
str = get_property(L"compatible");
|
||||
if (str) {
|
||||
efi_st_printf("compatible: %s\n", str);
|
||||
ret = boottime->free_pool(str);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("FreePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
} else {
|
||||
efi_st_printf("Missing property 'compatible'\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
str = get_property(L"serial-number");
|
||||
if (str) {
|
||||
efi_st_printf("serial-number: %s\n", str);
|
||||
ret = boottime->free_pool(str);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("FreePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(fdt) = {
|
||||
.name = "device tree",
|
||||
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup,
|
||||
.execute = execute,
|
||||
};
|
||||
@@ -0,0 +1,99 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_gop
|
||||
*
|
||||
* Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* Test the graphical output protocol.
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
|
||||
static struct efi_boot_services *boottime;
|
||||
static efi_guid_t efi_gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
|
||||
static struct efi_gop *gop;
|
||||
|
||||
/*
|
||||
* Setup unit test.
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int setup(const efi_handle_t handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
efi_status_t ret;
|
||||
|
||||
boottime = systable->boottime;
|
||||
|
||||
ret = boottime->locate_protocol(&efi_gop_guid, NULL, (void **)&gop);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
gop = NULL;
|
||||
efi_st_printf("Graphical output protocol is not available.\n");
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tear down unit test.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int teardown(void)
|
||||
{
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute unit test.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
efi_status_t ret;
|
||||
u32 i, max_mode;
|
||||
efi_uintn_t size;
|
||||
struct efi_gop_mode_info *info;
|
||||
|
||||
if (!gop)
|
||||
return EFI_ST_SUCCESS;
|
||||
|
||||
if (!gop->mode) {
|
||||
efi_st_error("EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE missing\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
max_mode = gop->mode->max_mode;
|
||||
if (!max_mode) {
|
||||
efi_st_error("No graphical mode available\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
efi_st_printf("Number of available modes: %u\n", max_mode);
|
||||
|
||||
for (i = 0; i < max_mode; ++i) {
|
||||
ret = gop->query_mode(gop, i, &size, &info);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_printf("Could not query mode %u\n", i);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
efi_st_printf("Mode %u: %u x %u\n",
|
||||
i, info->width, info->height);
|
||||
ret = boottime->free_pool(info);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_printf("FreePool failed");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(gop) = {
|
||||
.name = "graphical output",
|
||||
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup,
|
||||
.execute = execute,
|
||||
.teardown = teardown,
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,453 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* This file's test data is derived from UEFI SCT.
|
||||
* The original copyright is attached below.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2006 - 2016 Unified EFI, Inc.<BR>
|
||||
* Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.<BR>
|
||||
*
|
||||
* This program and the accompanying materials
|
||||
* are licensed and made available under the terms and conditions of the BSD
|
||||
* License which accompanies this distribution. The full text of the license
|
||||
* may be found at
|
||||
* http://opensource.org/licenses/bsd-license.php
|
||||
*
|
||||
* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
||||
*/
|
||||
|
||||
#include <efi.h>
|
||||
|
||||
#ifdef NOT_USED
|
||||
/*
|
||||
* TODO: These macro's are not used as they appear only in
|
||||
* "#ifdef NOT_USED" clauses. In the future, define them elsewhere.
|
||||
*/
|
||||
|
||||
/* HII form */
|
||||
#define EFI_IFR_AND_OP 0x15
|
||||
#define EFI_IFR_END_OP 0x29
|
||||
#define EFI_IFR_BITWISE_AND_OP 0x35
|
||||
|
||||
/* HII image */
|
||||
#define EFI_HII_IIBT_END 0x00
|
||||
#define EFI_HII_IIBT_IMAGE_1BIT 0x10
|
||||
#endif
|
||||
|
||||
/* HII keyboard layout */
|
||||
#define EFI_NULL_MODIFIER 0x0000
|
||||
|
||||
u8 packagelist1[] = {
|
||||
// EFI_HII_PACKAGE_LIST_HEADER, length = 20
|
||||
// SimpleFont, Font, GUID, Form, String, Image, DevicePath,
|
||||
// (74) (110) 20 (8) 78 (67) (8)
|
||||
// KeyboardLayout, End
|
||||
// 192 4
|
||||
|
||||
0x89, 0xcd, 0xab, 0x03, 0xf4, 0x03, 0x44, 0x70,
|
||||
0x81, 0xde, 0x99, 0xb1, 0x81, 0x20, 0xf7, 0x68, //16: guid
|
||||
0x3a, 0x01, 0x00, 0x00, // 4: total 314(0x13a)
|
||||
#ifdef NOT_USED /* TODO: simple font package not implemented yet */
|
||||
//
|
||||
// Simple Font Package 1, length = 74
|
||||
//
|
||||
0x4A, 0x00, 0x00,
|
||||
EFI_HII_PACKAGE_SIMPLE_FONTS,
|
||||
1, 0,
|
||||
1, 0,
|
||||
0x55, 0x0, 0x1,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
||||
0x77, 0x0, 0x2,
|
||||
2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
|
||||
3, 4, 5,
|
||||
6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 0, 0, 0,
|
||||
//
|
||||
// Font Package 1, length = 110
|
||||
//
|
||||
0x6e, 0x00, 0x00, // 3
|
||||
EFI_HII_PACKAGE_FONTS, // 1
|
||||
0x5c, 0x00, 0x00, 0x00, // 4: size of header
|
||||
0x5c, 0x00, 0x00, 0x00, // 4: offset
|
||||
0xf1, 0x00, 0xf2, 0x00, 0xf3, 0x00, 0xf4, 0x00,
|
||||
0xf5, 0x00, 0xec, 0xec, //10+2(pads)
|
||||
0xff, 0x33, 0xff, 0x44, // 4: font style
|
||||
0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, //64
|
||||
//
|
||||
// Glyph block 1, length = 18
|
||||
//
|
||||
EFI_HII_GIBT_GLYPH_DEFAULT, // 1
|
||||
0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x99,
|
||||
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, //16: BitMapData
|
||||
EFI_HII_GIBT_END, // 1
|
||||
#endif
|
||||
//
|
||||
// Guid Package 1, length = 20
|
||||
//
|
||||
0x14, 0x00, 0x00, // 3
|
||||
EFI_HII_PACKAGE_TYPE_GUID, // 1
|
||||
0x5a, 0xc9, 0x87, 0x03, 0x3, 0xd7, 0x46, 0x23,
|
||||
0xb2, 0xab, 0xd0, 0xc7, 0xdd, 0x90, 0x44, 0xf8, //16: guid
|
||||
#ifdef NOT_USED /* TODO: form package not implemented yet */
|
||||
//
|
||||
// EFI_HII_PACKAGE_FORMS, length = 8
|
||||
//
|
||||
0x08, 0x00, 0x00, // 3
|
||||
EFI_HII_PACKAGE_FORMS, // 1
|
||||
//
|
||||
// Opcode 1, length = 4
|
||||
//
|
||||
EFI_IFR_AND_OP,
|
||||
0x82,
|
||||
EFI_IFR_END_OP,
|
||||
0x02,
|
||||
//
|
||||
#endif
|
||||
// EFI_HII_PACKAGE_STRINGS, length = 78
|
||||
//
|
||||
0x4e, 0x00, 0x00, // 3: length(header)
|
||||
EFI_HII_PACKAGE_STRINGS, // 1: type(header)
|
||||
0x3c, 0x00, 0x00, 0x00, // 4: header_size
|
||||
0x3c, 0x00, 0x00, 0x00, // 4: string_offset
|
||||
0x00, 0x00, 0x11, 0x22, 0x44, 0x55, 0x77, 0x89, //32: language_window
|
||||
0x11, 0x00, 0x11, 0x22, 0x44, 0x55, 0x87, 0x89,
|
||||
0x22, 0x00, 0x11, 0x22, 0x44, 0x55, 0x77, 0x89,
|
||||
0x33, 0x00, 0x11, 0x22, 0x44, 0x55, 0x77, 0x89,
|
||||
0x01, 0x00, // 2: language name
|
||||
0x65, 0x6e, 0x2d, 0x55, 0x53, 0x3b, 0x7a, 0x68, //14: language
|
||||
0x2d, 0x48, 0x61, 0x6e, 0x74, 0x00, // "en-US;zh-Hant"
|
||||
EFI_HII_SIBT_STRING_UCS2, // 1
|
||||
0x45, 0x00, 0x6E, 0x00, 0x67, 0x00, 0x6C, 0x00,
|
||||
0x69, 0x00, 0x73, 0x00, 0x68, 0x00, 0x00, 0x00, //16: "English"
|
||||
EFI_HII_SIBT_END, // 1
|
||||
#ifdef NOT_USED /* TODO: image package not implemented yet */
|
||||
//
|
||||
// EFI_HII_PACKAGE_IMAGES, length = 67
|
||||
//
|
||||
0x43, 0x00, 0x00, // 3
|
||||
EFI_HII_PACKAGE_IMAGES, // 1
|
||||
0x0c, 0x00, 0x00, 0x00, // 4: image info offset
|
||||
0x39, 0x00, 0x00, 0x00, // 4: palette info offset
|
||||
EFI_HII_IIBT_IMAGE_1BIT, // 1
|
||||
0x01,
|
||||
0x0b, 0x00,
|
||||
0x13, 0x00,
|
||||
0x80, 0x00,
|
||||
0xc0, 0x00,
|
||||
0xe0, 0x00,
|
||||
0xf0, 0x00,
|
||||
0xf8, 0x00,
|
||||
0xfc, 0x00,
|
||||
0xfe, 0x00,
|
||||
0xff, 0x00,
|
||||
0xff, 0x80,
|
||||
0xff, 0xc0,
|
||||
0xff, 0xe0,
|
||||
0xfe, 0x00,
|
||||
0xef, 0x00,
|
||||
0xcf, 0x00,
|
||||
0x87, 0x80,
|
||||
0x07, 0x80,
|
||||
0x03, 0xc0,
|
||||
0x03, 0xc0,
|
||||
0x01, 0x80, //43
|
||||
EFI_HII_IIBT_END, // 1
|
||||
0x01, 0x00,
|
||||
0x06, 0x00,
|
||||
0x00, 0x00, 0x00,
|
||||
0xFF, 0xFF, 0xFF, //10
|
||||
//
|
||||
// EFI_HII_PACKAGE_DEVICE_PATH, length = 8
|
||||
//
|
||||
0x08, 0x00, 0x00, // 3
|
||||
EFI_HII_PACKAGE_DEVICE_PATH, // 1
|
||||
0x01, 0x23, 0x45, 0x66, // 4: dummy device path protocol
|
||||
// instance address
|
||||
#endif
|
||||
//
|
||||
// Keyboard layout package 1, length = 192
|
||||
0xc0, 0x00, 0x00, // 3: length(header)
|
||||
EFI_HII_PACKAGE_KEYBOARD_LAYOUT, // 1: type(header)
|
||||
0x02, 0x00, // 2: LayoutCount
|
||||
//
|
||||
// Layout 1, length = 93
|
||||
//
|
||||
0x5d, 0x00, // 2: layout_length
|
||||
0x95, 0xe4, 0x40, 0x8d, 0xaa, 0xe2, 0x6f, 0x4c,
|
||||
0x89, 0x70, 0x68, 0x85, 0x09, 0xee, 0xc7, 0xd2, //16: guid
|
||||
0x37, 0x00, 0x00, 0x00, // 4: layout_descriptor_
|
||||
// string_offset
|
||||
0x02, // 1: descriptor_count
|
||||
//
|
||||
// Descriptor 1, length = 16
|
||||
//
|
||||
49, 0x00, 0x00, 0x00, // 4: key (EfiKeyD1)
|
||||
'q', 0x00, // 2: unicode
|
||||
'Q', 0x00, // 2: shifted_unicode
|
||||
0x00, 0x00, // 2: alt_gr_unicode
|
||||
0x00, 0x00, // 2: shifted_alt_gr_unicode
|
||||
EFI_NULL_MODIFIER, 0x00, // 2: modifier
|
||||
0x03, 0x00, // 2: affected_attribute
|
||||
//
|
||||
// Descriptor 2, length = 16
|
||||
//
|
||||
50, 0x00, 0x00, 0x00, // 4: key (EfiKeyD2)
|
||||
'w', 0x00, // 2: unicode
|
||||
'W', 0x00, // 2: shifted_unicode
|
||||
0x00, 0x00, // 2: alt_gr_unicode
|
||||
0x00, 0x00, // 2: shifted_alt_gr_unicode
|
||||
EFI_NULL_MODIFIER, 0x00, // 2: modifier
|
||||
0x3, 0x0, // 2: affected_attribute
|
||||
//
|
||||
// EFI_DESCRIPTOR_STRING_BUNDLE, length = 38
|
||||
//
|
||||
0x01, 0x00, // 2: DescriptionCount
|
||||
'e', 0x0, 'n', 0x0, '-', 0x0, 'U', 0x0, 'S', 0x0,
|
||||
//10: RFC3066 language code
|
||||
' ', 0x0, // 2: Space
|
||||
'S', 0x0, 'i', 0x0, 'm', 0x0, 'p', 0x0, 'l', 0x0, 'e', 0x0,
|
||||
'1', 0x0, 'o', 0x0, 'n', 0x0, 'l', 0x0, 'y', 0x0, '\0', 0x0,
|
||||
//24: DescriptionString
|
||||
//
|
||||
// Layout 2, length = 93
|
||||
//
|
||||
0x5d, 0x00, // 2: layout_length
|
||||
0x3e, 0x0b, 0xe6, 0x2a, 0xd6, 0xb9, 0xd8, 0x49,
|
||||
0x9a, 0x16, 0xc2, 0x48, 0xf1, 0xeb, 0xa8, 0xdb, //16: guid
|
||||
0x37, 0x00, 0x00, 0x00, // 4: layout_descriptor_
|
||||
// string_offset
|
||||
0x02, // 1 Descriptor count
|
||||
//
|
||||
// Descriptor 1, length = 16
|
||||
//
|
||||
51, 0x0, 0x0, 0x0, // 4: key (EfiKeyD3)
|
||||
'e', 0x00, // 2: unicode
|
||||
'E', 0x00, // 2: shifted_unicode
|
||||
0x00, 0x00, // 2: alt_gr_unicode
|
||||
0x00, 0x00, // 2: shifted_alt_gr_unicode
|
||||
EFI_NULL_MODIFIER, 0x0, // 2: modifier
|
||||
0x3, 0x0, // 2: affected_attribute
|
||||
//
|
||||
// Descriptor 2, length = 16
|
||||
//
|
||||
52, 0x0, 0x0, 0x0, // 4: key (EfiKeyD4)
|
||||
'r', 0x00, // 2: unicode
|
||||
'R', 0x00, // 2: shifted_unicode
|
||||
0x00, 0x00, // 2: alt_gr_unicode
|
||||
0x00, 0x00, // 2: shifted_alt_gr_unicode
|
||||
EFI_NULL_MODIFIER, 0x0, // 2: modifier
|
||||
0x3, 0x0, // 2: affected_attribute
|
||||
//
|
||||
// EFI_DESCRIPTOR_STRING_BUNDLE, length = 38
|
||||
//
|
||||
0x01, 0x00, // 2: DescriptionCount
|
||||
'e', 0x0, 'n', 0x0, '-', 0x0, 'U', 0x0, 'S', 0x0,
|
||||
//10: RFC3066 language code
|
||||
' ', 0x0, // 2: Space
|
||||
'S', 0x0, 'i', 0x0, 'm', 0x0, 'p', 0x0, 'l', 0x0, 'e', 0x0,
|
||||
'2', 0x0, 'o', 0x0, 'n', 0x0, 'l', 0x0, 'y', 0x0, '\0', 0x0,
|
||||
//24: DescriptionString
|
||||
//
|
||||
// End of package list, length = 4
|
||||
//
|
||||
0x4, 0x00, 0x00,
|
||||
EFI_HII_PACKAGE_END
|
||||
};
|
||||
|
||||
u8 packagelist2[] = {
|
||||
// EFI_HII_PACKAGE_LIST_HEADER, length = 20
|
||||
// SimpleFont, Font, GUID, KeyboardLayout, Form, End
|
||||
// (74) (122) 20 192 (8) 4
|
||||
0xd3, 0xde, 0x85, 0x86, 0xce, 0x1b, 0xf3, 0x43,
|
||||
0xa2, 0x0c, 0xa3, 0x06, 0xec, 0x69, 0x72, 0xdd, //16
|
||||
0xec, 0x00, 0x00, 0x00, // 4: total 236(0xec)
|
||||
|
||||
#ifdef NOT_USED /* TODO: simple font package not implemented yet */
|
||||
//
|
||||
// Simple Font Package 2, length = 74
|
||||
//
|
||||
0x4A, 0x00, 0x00, // 3
|
||||
EFI_HII_PACKAGE_SIMPLE_FONTS, // 1
|
||||
1, 0, // 2
|
||||
1, 0, // 2
|
||||
0x33, 0x0, 0, 1, 2, 3, 4, 5, 0, 7, 8, 9,
|
||||
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, //22
|
||||
0x44, 0x0, 0x2, 2, 3, 4, 5, 6, 0, 8, 9,
|
||||
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, //22
|
||||
3, 4, 5, 6, 7, 8, 9, 10, 11, 9, 13,
|
||||
14, 15, 16, 17, 18, 19, 20, 21, 0, 0, 0, //22
|
||||
//
|
||||
// Font Package 2, length = 122
|
||||
//
|
||||
0x7A, 0x00, 0x00, // 3
|
||||
EFI_HII_PACKAGE_FONTS, // 1
|
||||
0x5C, 0x00, 0x00, 0x00, // 4: size of header
|
||||
0x5C, 0x00, 0x00, 0x00, // 4: dummy offset
|
||||
0xf1, 0x00, 0xf2, 0x00, 0xf3, 0x00, 0xf4, 0x00,
|
||||
0xf5, 0x00, 0xec, 0xec, //10+2(pads)
|
||||
0xff, 0x11, 0xff, 0x22, // 4: font style
|
||||
0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, //64
|
||||
//
|
||||
// Glyph block 1, length = 30
|
||||
//
|
||||
EFI_HII_GIBT_GLYPH, // 1
|
||||
0xf1, 0x00, 0xf2, 0x00, 0xf3, 0x00, 0xf4, 0x00,
|
||||
0xf5, 0x00, //10
|
||||
0xff, 0x01, // 2
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, //16: BitMapData
|
||||
EFI_HII_GIBT_END, // 1
|
||||
#endif
|
||||
//
|
||||
// Guid Package 1, length = 20
|
||||
//
|
||||
0x14, 0x00, 0x00, // 3
|
||||
EFI_HII_PACKAGE_TYPE_GUID, // 1
|
||||
0x5a, 0xc9, 0x87, 0x03, 0x3, 0xd7, 0x46, 0x23,
|
||||
0xb2, 0xab, 0xd0, 0xc7, 0xdd, 0x90, 0x44, 0xf8, //16: guid
|
||||
//
|
||||
// Keyboard layout package 2, length = 192
|
||||
0xc0, 0x00, 0x00, // 3
|
||||
EFI_HII_PACKAGE_KEYBOARD_LAYOUT, // 1
|
||||
0x02, 0x00, //0xec, 0xec, // 2: LayoutCount
|
||||
//
|
||||
// Layout 1, length = 93
|
||||
//
|
||||
0x5d, 0x00, // 2: layout_length
|
||||
0x1f, 0x6a, 0xf5, 0xe0, 0x6b, 0xdf, 0x7e, 0x4a,
|
||||
0xa3, 0x9a, 0xe7, 0xa5, 0x19, 0x15, 0x45, 0xd6,//16: guid
|
||||
0x37, 0x00, 0x00, 0x00, // 4: layout_descriptor
|
||||
// string offset
|
||||
0x02, // 1: descriptor_count
|
||||
//
|
||||
// Descriptor 1, length = 16
|
||||
//
|
||||
32, 0x00, 0x00, 0x00, // 4: key (EfiKeyC1)
|
||||
'a', 0x00, // 2: unicode
|
||||
'A', 0x00, // 2: shifted_unicode
|
||||
0x00, 0x00, // 2: alt_gr_unicode
|
||||
0x00, 0x00, // 2: shifted_alt_gr_unic
|
||||
EFI_NULL_MODIFIER, 0x00, // 2: modifier
|
||||
0x03, 0x00, // 2: affected_attribute
|
||||
//
|
||||
// Descriptor 2, length = 16
|
||||
//
|
||||
33 /*EfiKeyC2*/, 0x00, 0x00, 0x00,
|
||||
's', 0x00,
|
||||
'S', 0x00,
|
||||
0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
EFI_NULL_MODIFIER, 0x00,
|
||||
0x3, 0x0,
|
||||
//
|
||||
// EFI_DESCRIPTOR_STRING_BUNDLE, length = 38
|
||||
//
|
||||
0x01, 0x00, // 2: DescriptionCount
|
||||
'e', 0x0, 'n', 0x0, '-', 0x0, 'U', 0x0, 'S', 0x0,
|
||||
//10: RFC3066 language code
|
||||
' ', 0x0, // 2: Space
|
||||
'S', 0x0, 'i', 0x0, 'm', 0x0, 'p', 0x0, 'l', 0x0, 'e', 0x0,
|
||||
'3', 0x0, 'o', 0x0, 'n', 0x0, 'l', 0x0, 'y', 0x0, '\0', 0x0,
|
||||
//24: DescriptionString
|
||||
//
|
||||
// Layout 2, length = 93
|
||||
//
|
||||
0x5d, 0x00, // 2: layout_length
|
||||
0xc9, 0x6a, 0xbe, 0x47, 0xcc, 0x54, 0xf9, 0x46,
|
||||
0xa2, 0x62, 0xd5, 0x3b, 0x25, 0x6a, 0xc, 0x34, //16: guid
|
||||
0x37, 0x00, 0x00, 0x00, // 4: layout_descriptor
|
||||
// string_offset
|
||||
0x02, // 1: descriptor_count
|
||||
//
|
||||
// Descriptor 1, length = 16
|
||||
//
|
||||
34 /*EfiKeyC3*/, 0x0, 0x0, 0x0,
|
||||
'd', 0x00,
|
||||
'D', 0x00,
|
||||
0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
EFI_NULL_MODIFIER, 0x0,
|
||||
0x3, 0x0,
|
||||
//
|
||||
// Descriptor 2, length = 16
|
||||
//
|
||||
35 /*EfiKeyC4*/, 0x0, 0x0, 0x0,
|
||||
'e', 0x00,
|
||||
'E', 0x00,
|
||||
0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
EFI_NULL_MODIFIER, 0x0,
|
||||
0x3, 0x0,
|
||||
//
|
||||
// EFI_DESCRIPTOR_STRING_BUNDLE, length = 38
|
||||
//
|
||||
0x01, 0x00, // 2: DescriptionCount
|
||||
'e', 0x0, 'n', 0x0, '-', 0x0, 'U', 0x0, 'S', 0x0,
|
||||
//10: RFC3066 language code
|
||||
' ', 0x0, // 2: Space
|
||||
'S', 0x0, 'i', 0x0, 'm', 0x0, 'p', 0x0, 'l', 0x0, 'e', 0x0,
|
||||
'4', 0x0, 'o', 0x0, 'n', 0x0, 'l', 0x0, 'y', 0x0, '\0', 0x0,
|
||||
//24: DescriptionString
|
||||
#ifdef NOT_USED /* TODO: form package not implemented yet */
|
||||
//
|
||||
// EFI_HII_PACKAGE_FORMS, length = 8
|
||||
//
|
||||
0x08, 0x00, 0x00, // 3
|
||||
EFI_HII_PACKAGE_FORMS, // 1
|
||||
//
|
||||
// Opcode 1
|
||||
//
|
||||
EFI_IFR_BITWISE_AND_OP, // 1
|
||||
0x02, // 1
|
||||
EFI_IFR_END_OP, // 1
|
||||
0x02, // 1
|
||||
#endif
|
||||
//
|
||||
// End of package list, length = 4
|
||||
//
|
||||
0x4, 0x00, 0x00, // 3
|
||||
EFI_HII_PACKAGE_END // 1
|
||||
};
|
||||
|
||||
efi_guid_t packagelist_guid1 =
|
||||
EFI_GUID(0x03abcd89, 0x03f4, 0x7044,
|
||||
0x81, 0xde, 0x99, 0xb1, 0x81, 0x20, 0xf7, 0x68);
|
||||
|
||||
efi_guid_t packagelist_guid2 =
|
||||
EFI_GUID(0x8685ded3, 0x1bce, 0x43f3,
|
||||
0xa2, 0x0c, 0xa3, 0x06, 0xec, 0x69, 0x72, 0xdd);
|
||||
|
||||
efi_guid_t kb_layout_guid11 =
|
||||
EFI_GUID(0x8d40e495, 0xe2aa, 0x4c6f,
|
||||
0x89, 0x70, 0x68, 0x85, 0x09, 0xee, 0xc7, 0xd2);
|
||||
|
||||
efi_guid_t kb_layout_guid12 =
|
||||
EFI_GUID(0x2ae60b3e, 0xb9d6, 0x49d8,
|
||||
0x9a, 0x16, 0xc2, 0x48, 0xf1, 0xeb, 0xa8, 0xdb);
|
||||
|
||||
efi_guid_t kb_layout_guid21 =
|
||||
EFI_GUID(0xe0f56a1f, 0xdf6b, 0x4a7e,
|
||||
0xa3, 0x9a, 0xe7, 0xa5, 0x19, 0x15, 0x45, 0xd6);
|
||||
|
||||
efi_guid_t kb_layout_guid22 =
|
||||
EFI_GUID(0x47be6ac9, 0x54cc, 0x46f9,
|
||||
0xa2, 0x62, 0xd5, 0x3b, 0x25, 0x6a, 0x0c, 0x34);
|
||||
|
||||
efi_guid_t package_guid =
|
||||
EFI_GUID(0x0387c95a, 0xd703, 0x2346,
|
||||
0xb2, 0xab, 0xd0, 0xc7, 0xdd, 0x90, 0x44, 0xf8);
|
||||
@@ -0,0 +1,107 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_loaded_image
|
||||
*
|
||||
* Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* This unit test checks the Loaded Image Protocol.
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
|
||||
static efi_guid_t loaded_image_protocol_guid =
|
||||
EFI_GUID(0x5b1b31a1, 0x9562, 0x11d2,
|
||||
0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b);
|
||||
static struct efi_boot_services *boottime;
|
||||
efi_handle_t image_handle;
|
||||
|
||||
/*
|
||||
* Setup unit test.
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
*/
|
||||
static int setup(const efi_handle_t img_handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
boottime = systable->boottime;
|
||||
image_handle = img_handle;
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute unit test.
|
||||
*
|
||||
* Verify that the loaded image protocol is installed on the image handle.
|
||||
* Verify that the loaded image protocol points to the system table.
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
efi_status_t ret;
|
||||
efi_uintn_t i, protocol_buffer_count = 0;
|
||||
efi_guid_t **protocol_buffer = NULL;
|
||||
bool found = false;
|
||||
struct efi_loaded_image *loaded_image_protocol;
|
||||
|
||||
/*
|
||||
* Get the GUIDs of all protocols installed on the handle.
|
||||
*/
|
||||
ret = boottime->protocols_per_handle(image_handle, &protocol_buffer,
|
||||
&protocol_buffer_count);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("ProtocolsPerHandle failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (!protocol_buffer_count || !protocol_buffer) {
|
||||
efi_st_error("ProtocolsPerHandle returned no protocol\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
efi_st_printf("%u protocols installed on image handle\n",
|
||||
(unsigned int)protocol_buffer_count);
|
||||
for (i = 0; i < protocol_buffer_count; ++i) {
|
||||
if (memcmp(protocol_buffer[i], &loaded_image_protocol_guid,
|
||||
sizeof(efi_guid_t)))
|
||||
found = true;
|
||||
}
|
||||
if (!found) {
|
||||
efi_st_printf("LoadedImageProtocol not found\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->free_pool(protocol_buffer);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("FreePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Open the loaded image protocol.
|
||||
*/
|
||||
ret = boottime->open_protocol(image_handle, &loaded_image_protocol_guid,
|
||||
(void **)&loaded_image_protocol, NULL,
|
||||
NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("OpenProtocol failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (loaded_image_protocol->revision !=
|
||||
EFI_LOADED_IMAGE_PROTOCOL_REVISION) {
|
||||
efi_st_printf("Incorrect revision\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (!loaded_image_protocol->system_table ||
|
||||
loaded_image_protocol->system_table->hdr.signature !=
|
||||
EFI_SYSTEM_TABLE_SIGNATURE) {
|
||||
efi_st_printf("System table reference missing\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(loadedimage) = {
|
||||
.name = "loaded image",
|
||||
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup,
|
||||
.execute = execute,
|
||||
};
|
||||
@@ -0,0 +1,528 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_loadimage
|
||||
*
|
||||
* Copyright (c) 2019 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* This test checks the LoadImage and StartImage boot service.
|
||||
*
|
||||
* The efi_selftest_miniapp_exit.efi application is loaded via a file device
|
||||
* path and started.
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
/* Include containing the efi_selftest_miniapp_exit.efi application */
|
||||
#include "efi_miniapp_file_image_exit.h"
|
||||
|
||||
/* Block size of compressed disk image */
|
||||
#define COMPRESSED_DISK_IMAGE_BLOCK_SIZE 8
|
||||
|
||||
/* Binary logarithm of the block size */
|
||||
#define LB_BLOCK_SIZE 9
|
||||
|
||||
#define FILE_NAME L"app.efi"
|
||||
#define VOLUME_NAME L"EfiDisk"
|
||||
|
||||
static struct efi_boot_services *boottime;
|
||||
static efi_handle_t handle_image;
|
||||
static efi_handle_t handle_volume;
|
||||
|
||||
static const efi_guid_t guid_device_path = EFI_DEVICE_PATH_PROTOCOL_GUID;
|
||||
static const efi_guid_t guid_simple_file_system_protocol =
|
||||
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
|
||||
static const efi_guid_t guid_file_info = EFI_FILE_INFO_GUID;
|
||||
static const efi_guid_t guid_file_system_info = EFI_FILE_SYSTEM_INFO_GUID;
|
||||
|
||||
/* One 8 byte block of the compressed disk image */
|
||||
struct line {
|
||||
size_t addr;
|
||||
char *line;
|
||||
};
|
||||
|
||||
/* Compressed file image */
|
||||
struct compressed_file_image {
|
||||
size_t length;
|
||||
struct line lines[];
|
||||
};
|
||||
|
||||
/* File info including file name */
|
||||
struct file_info {
|
||||
struct efi_file_info info;
|
||||
u16 file_name[sizeof(FILE_NAME)];
|
||||
};
|
||||
|
||||
/* File system info including volume name */
|
||||
struct file_system_info {
|
||||
struct efi_file_system_info info;
|
||||
u16 file_name[sizeof(VOLUME_NAME)];
|
||||
};
|
||||
|
||||
/* Compressed file image */
|
||||
static struct compressed_file_image img = EFI_ST_DISK_IMG;
|
||||
|
||||
/* Pointer to decompressed file image */
|
||||
static u8 *image;
|
||||
|
||||
/* File info */
|
||||
static struct file_info priv_file_info = {
|
||||
{
|
||||
.size = sizeof(struct file_info),
|
||||
.attribute = EFI_FILE_READ_ONLY,
|
||||
},
|
||||
FILE_NAME,
|
||||
};
|
||||
|
||||
/* Pointer to file info */
|
||||
struct efi_file_info *file_info = &priv_file_info.info;
|
||||
|
||||
/* Volume device path */
|
||||
static struct {
|
||||
struct efi_device_path_vendor vendor;
|
||||
struct efi_device_path end;
|
||||
} __packed dp_volume = {
|
||||
.vendor = {
|
||||
.dp = {
|
||||
.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE,
|
||||
.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR,
|
||||
.length = sizeof(struct efi_device_path_vendor),
|
||||
},
|
||||
.guid = EFI_GUID(0x4f9a0ebf, 0xa179, 0x88a6, 0x25, 0x68,
|
||||
0x10, 0x72, 0xb1, 0x93, 0x51, 0x71),
|
||||
},
|
||||
.end = {
|
||||
.type = DEVICE_PATH_TYPE_END,
|
||||
.sub_type = DEVICE_PATH_SUB_TYPE_END,
|
||||
.length = sizeof(struct efi_device_path),
|
||||
}
|
||||
};
|
||||
|
||||
/* File device path */
|
||||
static struct {
|
||||
struct efi_device_path_vendor vendor;
|
||||
struct efi_device_path path;
|
||||
u16 file[sizeof(FILE_NAME)];
|
||||
struct efi_device_path end;
|
||||
} __packed dp_file = {
|
||||
.vendor = {
|
||||
.dp = {
|
||||
.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE,
|
||||
.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR,
|
||||
.length = sizeof(struct efi_device_path_vendor),
|
||||
},
|
||||
.guid = EFI_GUID(0x4f9a0ebf, 0xa179, 0x88a6, 0x25, 0x68,
|
||||
0x10, 0x72, 0xb1, 0x93, 0x51, 0x71),
|
||||
},
|
||||
.path = {
|
||||
.type = DEVICE_PATH_TYPE_MEDIA_DEVICE,
|
||||
.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH,
|
||||
.length = sizeof(struct efi_device_path) + sizeof(dp_file.file),
|
||||
},
|
||||
.file = FILE_NAME,
|
||||
.end = {
|
||||
.type = DEVICE_PATH_TYPE_END,
|
||||
.sub_type = DEVICE_PATH_SUB_TYPE_END,
|
||||
.length = sizeof(struct efi_device_path),
|
||||
}
|
||||
};
|
||||
|
||||
/* File system info */
|
||||
static struct file_system_info priv_file_system_info = {
|
||||
{
|
||||
.size = sizeof(struct file_system_info),
|
||||
.read_only = true,
|
||||
.volume_size = 0x100000,
|
||||
.free_space = 0x0,
|
||||
.block_size = 0x200,
|
||||
},
|
||||
VOLUME_NAME
|
||||
};
|
||||
|
||||
/* Pointer to file system info */
|
||||
static struct efi_file_system_info *file_system_info =
|
||||
&priv_file_system_info.info;
|
||||
|
||||
/* Forward definitions of file and file system functions */
|
||||
static efi_status_t EFIAPI open_volume
|
||||
(struct efi_simple_file_system_protocol *this,
|
||||
struct efi_file_handle **root);
|
||||
|
||||
static efi_status_t EFIAPI open
|
||||
(struct efi_file_handle *this,
|
||||
struct efi_file_handle **new_handle,
|
||||
u16 *file_name, u64 open_mode, u64 attributes);
|
||||
|
||||
static efi_status_t EFIAPI close(struct efi_file_handle *this);
|
||||
|
||||
static efi_status_t EFIAPI delete(struct efi_file_handle *this);
|
||||
|
||||
static efi_status_t EFIAPI read
|
||||
(struct efi_file_handle *this, efi_uintn_t *buffer_size, void *buffer);
|
||||
|
||||
static efi_status_t EFIAPI write
|
||||
(struct efi_file_handle *this, efi_uintn_t *buffer_size, void *buffer);
|
||||
|
||||
static efi_status_t EFIAPI getpos(struct efi_file_handle *this, u64 *pos);
|
||||
|
||||
static efi_status_t EFIAPI setpos(struct efi_file_handle *this, u64 pos);
|
||||
|
||||
static efi_status_t EFIAPI getinfo
|
||||
(struct efi_file_handle *this, const efi_guid_t *info_type,
|
||||
efi_uintn_t *buffer_size, void *buffer);
|
||||
|
||||
static efi_status_t EFIAPI setinfo
|
||||
(struct efi_file_handle *this, const efi_guid_t *info_type,
|
||||
efi_uintn_t buffer_size, void *buffer);
|
||||
|
||||
static efi_status_t EFIAPI flush(struct efi_file_handle *this);
|
||||
|
||||
/* Internal information about status of file system */
|
||||
static struct {
|
||||
/* Difference of volume open count minus volume close count */
|
||||
int volume_open_count;
|
||||
/* Difference of file open count minus file close count */
|
||||
int file_open_count;
|
||||
/* File size */
|
||||
u64 file_size;
|
||||
/* Current position in file */
|
||||
u64 file_pos;
|
||||
} priv;
|
||||
|
||||
/* EFI_FILE_PROTOCOL for file */
|
||||
static struct efi_file_handle file = {
|
||||
.rev = 0x00010000,
|
||||
.open = open,
|
||||
.close = close,
|
||||
.delete = delete,
|
||||
.read = read,
|
||||
.write = write,
|
||||
.getpos = getpos,
|
||||
.setpos = setpos,
|
||||
.getinfo = getinfo,
|
||||
.setinfo = setinfo,
|
||||
.flush = flush,
|
||||
};
|
||||
|
||||
/* EFI_FILE_PROTOCOL for root directory */
|
||||
static struct efi_file_handle volume = {
|
||||
.rev = 0x00010000,
|
||||
.open = open,
|
||||
.close = close,
|
||||
.delete = delete,
|
||||
.read = read,
|
||||
.write = write,
|
||||
.getpos = getpos,
|
||||
.setpos = setpos,
|
||||
.getinfo = getinfo,
|
||||
.setinfo = setinfo,
|
||||
.flush = flush,
|
||||
};
|
||||
|
||||
/* EFI_SIMPLE_FILE_SYSTEM_PROTOCOL of the block device */
|
||||
struct efi_simple_file_system_protocol file_system = {
|
||||
.rev = 0x00010000,
|
||||
.open_volume = open_volume,
|
||||
};
|
||||
|
||||
static efi_status_t EFIAPI open_volume
|
||||
(struct efi_simple_file_system_protocol *this,
|
||||
struct efi_file_handle **root)
|
||||
{
|
||||
if (this != &file_system || !root)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
*root = &volume;
|
||||
priv.volume_open_count++;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI open
|
||||
(struct efi_file_handle *this,
|
||||
struct efi_file_handle **new_handle,
|
||||
u16 *file_name, u64 open_mode, u64 attributes)
|
||||
{
|
||||
if (this != &volume)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
*new_handle = &file;
|
||||
priv.file_pos = 0;
|
||||
priv.file_open_count++;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI close(struct efi_file_handle *this)
|
||||
{
|
||||
if (this == &file)
|
||||
priv.file_open_count--;
|
||||
else if (this == &volume)
|
||||
priv.volume_open_count--;
|
||||
else
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI delete(struct efi_file_handle *this)
|
||||
{
|
||||
if (this != &file)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI read
|
||||
(struct efi_file_handle *this, efi_uintn_t *buffer_size, void *buffer)
|
||||
{
|
||||
if (this != &file)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
if (priv.file_pos >= img.length)
|
||||
*buffer_size = 0;
|
||||
else if (priv.file_pos + *buffer_size > img.length)
|
||||
*buffer_size = img.length - priv.file_pos;
|
||||
|
||||
boottime->copy_mem(buffer, &image[priv.file_pos], *buffer_size);
|
||||
priv.file_pos += *buffer_size;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI write
|
||||
(struct efi_file_handle *this, efi_uintn_t *buffer_size, void *buffer)
|
||||
{
|
||||
if (this != &file)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI getpos(struct efi_file_handle *this, u64 *pos)
|
||||
{
|
||||
if (this != &file)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
*pos = priv.file_pos;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI setpos(struct efi_file_handle *this, u64 pos)
|
||||
{
|
||||
if (this != &file)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
priv.file_pos = pos;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI getinfo
|
||||
(struct efi_file_handle *this, const efi_guid_t *info_type,
|
||||
efi_uintn_t *buffer_size, void *buffer)
|
||||
{
|
||||
if (this == &file) {
|
||||
if (memcmp(info_type, &guid_file_info, sizeof(efi_guid_t)))
|
||||
return EFI_INVALID_PARAMETER;
|
||||
if (*buffer_size >= sizeof(struct file_info)) {
|
||||
boottime->copy_mem(buffer, file_info,
|
||||
sizeof(struct file_info));
|
||||
} else {
|
||||
*buffer_size = sizeof(struct file_info);
|
||||
return EFI_BUFFER_TOO_SMALL;
|
||||
}
|
||||
} else if (this == &volume) {
|
||||
if (memcmp(info_type, &guid_file_system_info,
|
||||
sizeof(efi_guid_t)))
|
||||
return EFI_INVALID_PARAMETER;
|
||||
if (*buffer_size >= sizeof(struct file_system_info)) {
|
||||
boottime->copy_mem(buffer, file_system_info,
|
||||
sizeof(struct file_system_info));
|
||||
} else {
|
||||
*buffer_size = sizeof(struct file_system_info);
|
||||
return EFI_BUFFER_TOO_SMALL;
|
||||
}
|
||||
} else {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI setinfo
|
||||
(struct efi_file_handle *this, const efi_guid_t *info_type,
|
||||
efi_uintn_t buffer_size, void *buffer)
|
||||
{
|
||||
if (this != &file)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static efi_status_t EFIAPI flush(struct efi_file_handle *this)
|
||||
{
|
||||
if (this != &file)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decompress the disk image.
|
||||
*
|
||||
* @image decompressed disk image
|
||||
* @return status code
|
||||
*/
|
||||
static efi_status_t decompress(u8 **image)
|
||||
{
|
||||
u8 *buf;
|
||||
size_t i;
|
||||
size_t addr;
|
||||
size_t len;
|
||||
efi_status_t ret;
|
||||
|
||||
ret = boottime->allocate_pool(EFI_LOADER_DATA, img.length,
|
||||
(void **)&buf);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Out of memory\n");
|
||||
return ret;
|
||||
}
|
||||
boottime->set_mem(buf, img.length, 0);
|
||||
|
||||
for (i = 0; ; ++i) {
|
||||
if (!img.lines[i].line)
|
||||
break;
|
||||
addr = img.lines[i].addr;
|
||||
len = COMPRESSED_DISK_IMAGE_BLOCK_SIZE;
|
||||
if (addr + len > img.length)
|
||||
len = img.length - addr;
|
||||
boottime->copy_mem(buf + addr, img.lines[i].line, len);
|
||||
}
|
||||
*image = buf;
|
||||
priv.file_size = img.length;
|
||||
file_info->file_size = img.length;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup unit test.
|
||||
*
|
||||
* Decompress application image and provide a handle for the in memory block
|
||||
* device.
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int setup(const efi_handle_t handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
efi_status_t ret;
|
||||
|
||||
handle_image = handle;
|
||||
boottime = systable->boottime;
|
||||
|
||||
/* Load the application image into memory */
|
||||
decompress(&image);
|
||||
|
||||
ret = boottime->install_protocol_interface
|
||||
(&handle_volume, &guid_device_path, EFI_NATIVE_INTERFACE,
|
||||
&dp_volume);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to install device path\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->install_protocol_interface
|
||||
(&handle_volume, &guid_simple_file_system_protocol,
|
||||
EFI_NATIVE_INTERFACE, &file_system);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to install simple file system protocol\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tear down unit test.
|
||||
*
|
||||
* Uninstall protocols and free memory.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int teardown(void)
|
||||
{
|
||||
efi_status_t ret = EFI_ST_SUCCESS;
|
||||
|
||||
if (handle_volume) {
|
||||
ret = boottime->uninstall_protocol_interface
|
||||
(handle_volume, &guid_simple_file_system_protocol,
|
||||
&file_system);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error
|
||||
("Failed to uninstall simple file system protocol\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->uninstall_protocol_interface
|
||||
(handle_volume, &guid_device_path, &dp_volume);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error
|
||||
("Failed to uninstall device path protocol\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (image) {
|
||||
ret = boottime->free_pool(image);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to free image\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute unit test.
|
||||
*
|
||||
* Load and start the application image.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
efi_status_t ret;
|
||||
efi_handle_t handle;
|
||||
|
||||
ret = boottime->load_image(false, handle_image, &dp_file.vendor.dp,
|
||||
NULL, 0, &handle);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to load image\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->start_image(handle, NULL, NULL);
|
||||
if (ret != EFI_UNSUPPORTED) {
|
||||
efi_st_error("Wrong return value from application\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
if (priv.file_open_count) {
|
||||
efi_st_error("File open count = %d, expected 0\n",
|
||||
priv.file_open_count);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (priv.volume_open_count) {
|
||||
efi_st_error("Volume open count = %d, expected 0\n",
|
||||
priv.volume_open_count);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(loadimage) = {
|
||||
.name = "load image from file",
|
||||
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup,
|
||||
.execute = execute,
|
||||
.teardown = teardown,
|
||||
};
|
||||
@@ -0,0 +1,382 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_manageprotocols
|
||||
*
|
||||
* Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* This unit test checks the following protocol services:
|
||||
* InstallProtocolInterface, UninstallProtocolInterface,
|
||||
* InstallMultipleProtocolsInterfaces, UninstallMultipleProtocolsInterfaces,
|
||||
* HandleProtocol, ProtocolsPerHandle,
|
||||
* LocateHandle, LocateHandleBuffer.
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
|
||||
/*
|
||||
* The test currently does not actually call the interface function.
|
||||
* So this is just a dummy structure.
|
||||
*/
|
||||
struct interface {
|
||||
void (EFIAPI * inc)(void);
|
||||
};
|
||||
|
||||
static struct efi_boot_services *boottime;
|
||||
static efi_guid_t guid1 =
|
||||
EFI_GUID(0x2e7ca819, 0x21d3, 0x0a3a,
|
||||
0xf7, 0x91, 0x82, 0x1f, 0x7a, 0x83, 0x67, 0xaf);
|
||||
static efi_guid_t guid2 =
|
||||
EFI_GUID(0xf909f2bb, 0x90a8, 0x0d77,
|
||||
0x94, 0x0c, 0x3e, 0xa8, 0xea, 0x38, 0xd6, 0x6f);
|
||||
static efi_guid_t guid3 =
|
||||
EFI_GUID(0x06d641a3, 0xf4e7, 0xe0c9,
|
||||
0xe7, 0x8d, 0x41, 0x2d, 0x72, 0xa6, 0xb1, 0x24);
|
||||
static efi_handle_t handle1;
|
||||
static efi_handle_t handle2;
|
||||
static struct interface interface1;
|
||||
static struct interface interface2;
|
||||
static struct interface interface3;
|
||||
static struct interface interface4;
|
||||
|
||||
/*
|
||||
* Find a handle in an array.
|
||||
*
|
||||
* @handle: handle to find
|
||||
* @count: number of entries in the array
|
||||
* @buffer: array to search
|
||||
*/
|
||||
efi_status_t find_in_buffer(efi_handle_t handle, size_t count,
|
||||
efi_handle_t *buffer)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
if (buffer[i] == handle)
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
return EFI_NOT_FOUND;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup unit test.
|
||||
*
|
||||
* Create two handles and install two out of three protocol interfaces on each
|
||||
* of them:
|
||||
*
|
||||
* handle1
|
||||
* guid1 interface1
|
||||
* guid3 interface3
|
||||
* handle2
|
||||
* guid1 interface4
|
||||
* guid2 interface2
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
*/
|
||||
static int setup(const efi_handle_t img_handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
efi_status_t ret;
|
||||
efi_handle_t handle;
|
||||
|
||||
boottime = systable->boottime;
|
||||
|
||||
ret = boottime->install_protocol_interface(&handle1, &guid3,
|
||||
EFI_NATIVE_INTERFACE,
|
||||
&interface3);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("InstallProtocolInterface failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (!handle1) {
|
||||
efi_st_error("InstallProtocolInterface failed to create handle\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
handle = handle1;
|
||||
ret = boottime->install_protocol_interface(&handle1, &guid1,
|
||||
EFI_NATIVE_INTERFACE,
|
||||
&interface1);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("InstallProtocolInterface failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (handle != handle1) {
|
||||
efi_st_error("InstallProtocolInterface failed to use handle\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->install_multiple_protocol_interfaces(&handle2,
|
||||
&guid1, &interface4, &guid2, &interface2, NULL);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("InstallMultipleProtocolInterfaces failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (!handle2 || handle1 == handle2) {
|
||||
efi_st_error("InstallMultipleProtocolInterfaces failed to create handle\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tear down unit test.
|
||||
*
|
||||
*/
|
||||
static int teardown(void)
|
||||
{
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute unit test.
|
||||
*
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
struct interface *interface;
|
||||
efi_status_t ret;
|
||||
efi_handle_t *buffer;
|
||||
size_t buffer_size;
|
||||
efi_uintn_t count = 0;
|
||||
efi_guid_t **prot_buffer;
|
||||
efi_uintn_t prot_count;
|
||||
|
||||
/*
|
||||
* Test HandleProtocol
|
||||
*/
|
||||
ret = boottime->handle_protocol(handle1, &guid3, (void **)&interface);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("HandleProtocol failed to retrieve interface\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (interface != &interface3) {
|
||||
efi_st_error("HandleProtocol returned wrong interface\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->handle_protocol(handle1, &guid2, (void **)&interface);
|
||||
if (ret == EFI_SUCCESS) {
|
||||
efi_st_error("HandleProtocol returned not installed interface\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Test LocateHandleBuffer with AllHandles
|
||||
*/
|
||||
ret = boottime->locate_handle_buffer(ALL_HANDLES, NULL, NULL,
|
||||
&count, &buffer);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("LocateHandleBuffer with AllHandles failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
buffer_size = count;
|
||||
ret = find_in_buffer(handle1, count, buffer);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("LocateHandleBuffer failed to locate new handle\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = find_in_buffer(handle2, count, buffer);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("LocateHandleBuffer failed to locate new handle\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Release buffer */
|
||||
ret = boottime->free_pool(buffer);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("FreePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Test error handling in UninstallMultipleProtocols
|
||||
*
|
||||
* These are the installed protocol interfaces on handle 2:
|
||||
*
|
||||
* guid1 interface4
|
||||
* guid2 interface2
|
||||
*
|
||||
* Try to uninstall more protocols than there are installed. This
|
||||
* should return an error EFI_INVALID_PARAMETER. All deleted protocols
|
||||
* should be reinstalled.
|
||||
*/
|
||||
ret = boottime->uninstall_multiple_protocol_interfaces(
|
||||
handle2,
|
||||
&guid1, &interface4,
|
||||
&guid2, &interface2,
|
||||
&guid3, &interface3,
|
||||
NULL);
|
||||
if (ret != EFI_INVALID_PARAMETER) {
|
||||
printf("%lx", ret);
|
||||
efi_st_error("UninstallMultipleProtocolInterfaces did not catch error\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Test LocateHandleBuffer with ByProtocol
|
||||
*
|
||||
* These are the handles with a guid1 protocol interface installed:
|
||||
*
|
||||
* handle1, handle2
|
||||
*/
|
||||
count = buffer_size;
|
||||
ret = boottime->locate_handle_buffer(BY_PROTOCOL, &guid1, NULL,
|
||||
&count, &buffer);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("LocateHandleBuffer failed to locate new handles\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (count != 2) {
|
||||
efi_st_error("UninstallMultipleProtocolInterfaces deleted handle\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = find_in_buffer(handle1, count, buffer);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("LocateHandleBuffer failed to locate new handle\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = find_in_buffer(handle2, count, buffer);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("LocateHandleBuffer failed to locate new handle\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Clear the buffer, we are reusing it it the next step. */
|
||||
boottime->set_mem(buffer, sizeof(efi_handle_t) * buffer_size, 0);
|
||||
|
||||
/*
|
||||
* Test LocateHandle with ByProtocol
|
||||
*/
|
||||
count = buffer_size * sizeof(efi_handle_t);
|
||||
ret = boottime->locate_handle(BY_PROTOCOL, &guid1, NULL,
|
||||
&count, buffer);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("LocateHandle with ByProtocol failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (count / sizeof(efi_handle_t) != 2) {
|
||||
efi_st_error("LocateHandle failed to locate new handles\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
buffer_size = count;
|
||||
ret = find_in_buffer(handle1, count, buffer);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("LocateHandle failed to locate new handles\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = find_in_buffer(handle2, count, buffer);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("LocateHandle failed to locate new handles\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Release buffer */
|
||||
ret = boottime->free_pool(buffer);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("FreePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Test LocateProtocol
|
||||
*/
|
||||
ret = boottime->locate_protocol(&guid1, NULL, (void **)&interface);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("LocateProtocol failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (interface != &interface1 && interface != &interface4) {
|
||||
efi_st_error("LocateProtocol failed to locate protocol\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Test UninstallMultipleProtocols
|
||||
*/
|
||||
ret = boottime->uninstall_multiple_protocol_interfaces(
|
||||
handle2,
|
||||
&guid1, &interface4,
|
||||
&guid2, &interface2,
|
||||
NULL);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("UninstallMultipleProtocolInterfaces failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/*
|
||||
* Check that the protocols are really uninstalled.
|
||||
*/
|
||||
count = buffer_size;
|
||||
ret = boottime->locate_handle_buffer(BY_PROTOCOL, &guid1, NULL,
|
||||
&count, &buffer);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("LocateHandleBuffer failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (count != 1) {
|
||||
efi_st_error("UninstallMultipleProtocolInterfaces failed to uninstall protocols\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = find_in_buffer(handle1, count, buffer);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to locate new handle\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
boottime->set_mem(buffer, sizeof(efi_handle_t) * buffer_size, 0);
|
||||
|
||||
/*
|
||||
* Test ProtocolsPerHandle
|
||||
*/
|
||||
ret = boottime->protocols_per_handle(handle1,
|
||||
&prot_buffer, &prot_count);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to get protocols per handle\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (prot_count != 2) {
|
||||
efi_st_error("Failed to get protocols per handle\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (memcmp(prot_buffer[0], &guid1, 16) &&
|
||||
memcmp(prot_buffer[1], &guid1, 16)) {
|
||||
efi_st_error("Failed to get protocols per handle\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (memcmp(prot_buffer[0], &guid3, 16) &&
|
||||
memcmp(prot_buffer[1], &guid3, 16)) {
|
||||
efi_st_error("Failed to get protocols per handle\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Release buffer */
|
||||
ret = boottime->free_pool(prot_buffer);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("FreePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Uninstall remaining protocols
|
||||
*/
|
||||
ret = boottime->uninstall_protocol_interface(handle1, &guid1,
|
||||
&interface1);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("UninstallProtocolInterface failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->handle_protocol(handle1, &guid1, (void **)&interface);
|
||||
if (ret == EFI_SUCCESS) {
|
||||
efi_st_error("UninstallProtocolInterface failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->uninstall_protocol_interface(handle1, &guid3,
|
||||
&interface3);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("UninstallProtocolInterface failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(protserv) = {
|
||||
.name = "manage protocols",
|
||||
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup,
|
||||
.execute = execute,
|
||||
.teardown = teardown,
|
||||
};
|
||||
@@ -0,0 +1,192 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_memory
|
||||
*
|
||||
* Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* This unit test checks the following boottime services:
|
||||
* AllocatePages, FreePages, GetMemoryMap
|
||||
*
|
||||
* The memory type used for the device tree is checked.
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
|
||||
#define EFI_ST_NUM_PAGES 8
|
||||
|
||||
static const efi_guid_t fdt_guid = EFI_FDT_GUID;
|
||||
static struct efi_boot_services *boottime;
|
||||
static u64 fdt_addr;
|
||||
|
||||
/**
|
||||
* setup() - setup unit test
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
* Return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int setup(const efi_handle_t handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
boottime = systable->boottime;
|
||||
|
||||
for (i = 0; i < systable->nr_tables; ++i) {
|
||||
if (!memcmp(&systable->tables[i].guid, &fdt_guid,
|
||||
sizeof(efi_guid_t))) {
|
||||
if (fdt_addr) {
|
||||
efi_st_error("Duplicate device tree\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
fdt_addr = (uintptr_t)systable->tables[i].table;
|
||||
}
|
||||
}
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* find_in_memory_map() - check matching memory map entry exists
|
||||
*
|
||||
* @memory_map: memory map
|
||||
* @desc_size: number of memory map entries
|
||||
* @addr: physical address to find in the map
|
||||
* @type: expected memory type
|
||||
* Return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int find_in_memory_map(efi_uintn_t map_size,
|
||||
struct efi_mem_desc *memory_map,
|
||||
efi_uintn_t desc_size,
|
||||
u64 addr, int memory_type)
|
||||
{
|
||||
efi_uintn_t i;
|
||||
bool found = false;
|
||||
|
||||
for (i = 0; map_size; ++i, map_size -= desc_size) {
|
||||
struct efi_mem_desc *entry = &memory_map[i];
|
||||
|
||||
if (entry->physical_start != entry->virtual_start) {
|
||||
efi_st_error("Physical and virtual addresses do not match\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
if (addr >= entry->physical_start &&
|
||||
addr < entry->physical_start +
|
||||
(entry->num_pages << EFI_PAGE_SHIFT)) {
|
||||
if (found) {
|
||||
efi_st_error("Duplicate memory map entry\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
found = true;
|
||||
if (memory_type != entry->type) {
|
||||
efi_st_error
|
||||
("Wrong memory type %d, expected %d\n",
|
||||
entry->type, memory_type);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
efi_st_error("Missing memory map entry\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* execute() - execute unit test
|
||||
*
|
||||
* Return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
u64 p1;
|
||||
u64 p2;
|
||||
efi_uintn_t map_size = 0;
|
||||
efi_uintn_t map_key;
|
||||
efi_uintn_t desc_size;
|
||||
u32 desc_version;
|
||||
struct efi_mem_desc *memory_map;
|
||||
efi_status_t ret;
|
||||
|
||||
/* Allocate two page ranges with different memory type */
|
||||
ret = boottime->allocate_pages(EFI_ALLOCATE_ANY_PAGES,
|
||||
EFI_RUNTIME_SERVICES_CODE,
|
||||
EFI_ST_NUM_PAGES, &p1);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("AllocatePages did not return EFI_SUCCESS\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->allocate_pages(EFI_ALLOCATE_ANY_PAGES,
|
||||
EFI_RUNTIME_SERVICES_DATA,
|
||||
EFI_ST_NUM_PAGES, &p2);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("AllocatePages did not return EFI_SUCCESS\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/* Load memory map */
|
||||
ret = boottime->get_memory_map(&map_size, NULL, &map_key, &desc_size,
|
||||
&desc_version);
|
||||
if (ret != EFI_BUFFER_TOO_SMALL) {
|
||||
efi_st_error
|
||||
("GetMemoryMap did not return EFI_BUFFER_TOO_SMALL\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Allocate extra space for newly allocated memory */
|
||||
map_size += sizeof(struct efi_mem_desc);
|
||||
ret = boottime->allocate_pool(EFI_BOOT_SERVICES_DATA, map_size,
|
||||
(void **)&memory_map);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("AllocatePool did not return EFI_SUCCESS\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->get_memory_map(&map_size, memory_map, &map_key,
|
||||
&desc_size, &desc_version);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("GetMemoryMap did not return EFI_SUCCESS\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/* Check memory map entries */
|
||||
if (find_in_memory_map(map_size, memory_map, desc_size, p1,
|
||||
EFI_RUNTIME_SERVICES_CODE) != EFI_ST_SUCCESS)
|
||||
return EFI_ST_FAILURE;
|
||||
if (find_in_memory_map(map_size, memory_map, desc_size, p2,
|
||||
EFI_RUNTIME_SERVICES_DATA) != EFI_ST_SUCCESS)
|
||||
return EFI_ST_FAILURE;
|
||||
|
||||
/* Free memory */
|
||||
ret = boottime->free_pages(p1, EFI_ST_NUM_PAGES);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("FreePages did not return EFI_SUCCESS\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->free_pages(p2, EFI_ST_NUM_PAGES);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("FreePages did not return EFI_SUCCESS\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->free_pool(memory_map);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("FreePool did not return EFI_SUCCESS\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/* Check memory reservation for the device tree */
|
||||
if (fdt_addr &&
|
||||
find_in_memory_map(map_size, memory_map, desc_size, fdt_addr,
|
||||
EFI_BOOT_SERVICES_DATA) != EFI_ST_SUCCESS) {
|
||||
efi_st_error
|
||||
("Device tree not marked as boot services data\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(memory) = {
|
||||
.name = "memory",
|
||||
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup,
|
||||
.execute = execute,
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_miniapp_return
|
||||
*
|
||||
* Copyright (c) 2019 Heinrich Schuchardt
|
||||
*
|
||||
* This EFI application triggers an exception.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <efi_api.h>
|
||||
|
||||
/*
|
||||
* Entry point of the EFI application.
|
||||
*
|
||||
* @handle handle of the loaded image
|
||||
* @systable system table
|
||||
* @return status code
|
||||
*/
|
||||
efi_status_t EFIAPI efi_main(efi_handle_t handle,
|
||||
struct efi_system_table *systable)
|
||||
{
|
||||
struct efi_simple_text_output_protocol *con_out = systable->con_out;
|
||||
|
||||
con_out->output_string(con_out,
|
||||
L"EFI application triggers exception.\n");
|
||||
|
||||
#if defined(CONFIG_ARM)
|
||||
/*
|
||||
* 0xe7f...f. is undefined in ARM mode
|
||||
* 0xde.. is undefined in Thumb mode
|
||||
*/
|
||||
asm volatile (".word 0xe7f7defb\n");
|
||||
#elif defined(CONFIG_RISCV)
|
||||
asm volatile (".word 0xffffffff\n");
|
||||
#elif defined(CONFIG_X86)
|
||||
asm volatile (".word 0xffff\n");
|
||||
#endif
|
||||
con_out->output_string(con_out, L"Exception not triggered.\n");
|
||||
return EFI_ABORTED;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_miniapp_exit
|
||||
*
|
||||
* Copyright (c) 2018 Heinrich Schuchardt
|
||||
*
|
||||
* This EFI application is run by the StartImage selftest.
|
||||
* It uses the Exit boot service to return.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <efi_selftest.h>
|
||||
|
||||
static efi_guid_t loaded_image_protocol_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
|
||||
|
||||
/**
|
||||
* check_loaded_image_protocol() - check image_base/image_size
|
||||
*
|
||||
* Try to open the loaded image protocol. Check that this function is located
|
||||
* between image_base and image_base + image_size.
|
||||
*
|
||||
* @image_handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
* @return: status code
|
||||
*/
|
||||
static efi_status_t EFIAPI check_loaded_image_protocol
|
||||
(efi_handle_t image_handle, struct efi_system_table *systable)
|
||||
{
|
||||
struct efi_simple_text_output_protocol *cout = systable->con_out;
|
||||
struct efi_boot_services *boottime = systable->boottime;
|
||||
struct efi_loaded_image *loaded_image_protocol;
|
||||
efi_status_t ret;
|
||||
|
||||
/*
|
||||
* Open the loaded image protocol.
|
||||
*/
|
||||
ret = boottime->open_protocol
|
||||
(image_handle, &loaded_image_protocol_guid,
|
||||
(void **)&loaded_image_protocol, NULL,
|
||||
NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
cout->output_string(cout,
|
||||
L"Could not open loaded image protocol");
|
||||
return ret;
|
||||
}
|
||||
if ((void *)check_loaded_image_protocol <
|
||||
loaded_image_protocol->image_base ||
|
||||
(void *)check_loaded_image_protocol >=
|
||||
loaded_image_protocol->image_base +
|
||||
loaded_image_protocol->image_size) {
|
||||
cout->output_string(cout,
|
||||
L"Incorrect image_base or image_size\n");
|
||||
return EFI_NOT_FOUND;
|
||||
}
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point of the EFI application.
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
* @return: status code
|
||||
*/
|
||||
efi_status_t EFIAPI efi_main(efi_handle_t handle,
|
||||
struct efi_system_table *systable)
|
||||
{
|
||||
struct efi_simple_text_output_protocol *con_out = systable->con_out;
|
||||
efi_status_t ret;
|
||||
u16 text[] = EFI_ST_SUCCESS_STR;
|
||||
|
||||
con_out->output_string(con_out, L"EFI application calling Exit\n");
|
||||
|
||||
if (check_loaded_image_protocol(handle, systable) != EFI_SUCCESS) {
|
||||
con_out->output_string(con_out,
|
||||
L"Loaded image protocol missing\n");
|
||||
ret = EFI_NOT_FOUND;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* This return value is expected by the calling test */
|
||||
ret = EFI_UNSUPPORTED;
|
||||
out:
|
||||
systable->boottime->exit(handle, ret, sizeof(text), text);
|
||||
|
||||
/*
|
||||
* This statement should not be reached.
|
||||
* To enable testing use a different return value.
|
||||
*/
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_miniapp_return
|
||||
*
|
||||
* Copyright (c) 2018 Heinrich Schuchardt
|
||||
*
|
||||
* This EFI application is run by the StartImage selftest.
|
||||
* It returns directly without calling the Exit boot service.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <efi_api.h>
|
||||
|
||||
/*
|
||||
* Entry point of the EFI application.
|
||||
*
|
||||
* @handle handle of the loaded image
|
||||
* @systable system table
|
||||
* @return status code
|
||||
*/
|
||||
efi_status_t EFIAPI efi_main(efi_handle_t handle,
|
||||
struct efi_system_table *systable)
|
||||
{
|
||||
struct efi_simple_text_output_protocol *con_out = systable->con_out;
|
||||
|
||||
con_out->output_string(con_out,
|
||||
L"EFI application returning w/o calling Exit\n");
|
||||
|
||||
/* The return value is checked by the calling test */
|
||||
return EFI_INCOMPATIBLE_VERSION;
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_open_protocol
|
||||
*
|
||||
* Copyright (c) 2019 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* This unit test checks that open protocol information is correctly updated
|
||||
* when calling:
|
||||
* HandleProtocol, OpenProtocol, OpenProtocolInformation, CloseProtocol.
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
|
||||
/*
|
||||
* The test currently does not actually call the interface function.
|
||||
* So this is just a dummy structure.
|
||||
*/
|
||||
struct interface {
|
||||
void (EFIAPI *inc)(void);
|
||||
};
|
||||
|
||||
static struct efi_boot_services *boottime;
|
||||
static efi_guid_t guid1 =
|
||||
EFI_GUID(0x492a0e38, 0x1442, 0xf819,
|
||||
0x14, 0xaa, 0x4b, 0x8d, 0x09, 0xfe, 0x5a, 0xb9);
|
||||
static efi_handle_t handle1;
|
||||
static struct interface interface1;
|
||||
|
||||
/*
|
||||
* Setup unit test.
|
||||
*
|
||||
* Create a handle and install a protocol interface on it.
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
*/
|
||||
static int setup(const efi_handle_t img_handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
efi_status_t ret;
|
||||
|
||||
boottime = systable->boottime;
|
||||
|
||||
ret = boottime->install_protocol_interface(&handle1, &guid1,
|
||||
EFI_NATIVE_INTERFACE,
|
||||
&interface1);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("InstallProtocolInterface failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (!handle1) {
|
||||
efi_st_error
|
||||
("InstallProtocolInterface failed to create handle\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tear down unit test.
|
||||
*
|
||||
*/
|
||||
static int teardown(void)
|
||||
{
|
||||
efi_status_t ret;
|
||||
|
||||
if (handle1) {
|
||||
ret = boottime->uninstall_protocol_interface(handle1, &guid1,
|
||||
&interface1);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("UninstallProtocolInterface failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
}
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute unit test.
|
||||
*
|
||||
* Open the installed protocol twice via HandleProtocol() and once via
|
||||
* OpenProtocol(EFI_OPEN_PROTOCOL_GET_PROTOCOL). Read the open protocol
|
||||
* information and check the open counts. Finally close the protocol and
|
||||
* check again.
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
void *interface;
|
||||
struct efi_open_protocol_info_entry *entry_buffer;
|
||||
efi_uintn_t entry_count;
|
||||
efi_handle_t firmware_handle;
|
||||
efi_status_t ret;
|
||||
|
||||
ret = boottime->handle_protocol(handle1, &guid1, &interface);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("HandleProtocol failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (interface != &interface1) {
|
||||
efi_st_error("HandleProtocol returned wrong interface\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->open_protocol_information(handle1, &guid1,
|
||||
&entry_buffer, &entry_count);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("OpenProtocolInformation failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (entry_count != 1) {
|
||||
efi_st_error("Incorrect OpenProtocolInformation count\n");
|
||||
efi_st_printf("Expected 1, got %u\n",
|
||||
(unsigned int)entry_count);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->free_pool(entry_buffer);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("FreePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->handle_protocol(handle1, &guid1, &interface);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("HandleProtocol failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->open_protocol_information(handle1, &guid1,
|
||||
&entry_buffer, &entry_count);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("OpenProtocolInformation failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (entry_count != 1) {
|
||||
efi_st_error("Incorrect OpenProtocolInformation count\n");
|
||||
efi_st_printf("Expected 1, got %u\n",
|
||||
(unsigned int)entry_count);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (entry_buffer[0].open_count != 2) {
|
||||
efi_st_error("Incorrect open count: expected 2 got %u\n",
|
||||
entry_buffer[0].open_count);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
firmware_handle = entry_buffer[0].agent_handle;
|
||||
ret = boottime->free_pool(entry_buffer);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("FreePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->open_protocol(handle1, &guid1, &interface,
|
||||
firmware_handle, NULL,
|
||||
EFI_OPEN_PROTOCOL_GET_PROTOCOL);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("OpenProtocol failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->open_protocol_information(handle1, &guid1,
|
||||
&entry_buffer, &entry_count);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("OpenProtocolInformation failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (entry_count != 2) {
|
||||
efi_st_error("Incorrect OpenProtocolInformation count\n");
|
||||
efi_st_printf("Expected 2, got %u\n",
|
||||
(unsigned int)entry_count);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (entry_buffer[0].open_count + entry_buffer[1].open_count != 3) {
|
||||
efi_st_error("Incorrect open count: expected 3 got %u\n",
|
||||
entry_buffer[0].open_count +
|
||||
entry_buffer[1].open_count);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->free_pool(entry_buffer);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("FreePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->close_protocol(handle1, &guid1, firmware_handle, NULL);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("CloseProtocol failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->open_protocol_information(handle1, &guid1,
|
||||
&entry_buffer, &entry_count);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("OpenProtocolInformation failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (entry_count) {
|
||||
efi_st_error("Incorrect OpenProtocolInformation count\n");
|
||||
efi_st_printf("Expected 0, got %u\n",
|
||||
(unsigned int)entry_count);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(openprot) = {
|
||||
.name = "open protocol",
|
||||
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup,
|
||||
.execute = execute,
|
||||
.teardown = teardown,
|
||||
};
|
||||
@@ -0,0 +1,236 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_register_notify
|
||||
*
|
||||
* Copyright (c) 2019 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* This unit test checks the following protocol services:
|
||||
* InstallProtocolInterface, UninstallProtocolInterface,
|
||||
* RegisterProtocolNotify, CreateEvent, CloseEvent.
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
|
||||
/*
|
||||
* The test currently does not actually call the interface function.
|
||||
* So this is just a dummy structure.
|
||||
*/
|
||||
struct interface {
|
||||
void (EFIAPI * inc)(void);
|
||||
};
|
||||
|
||||
struct context {
|
||||
void *registration_key;
|
||||
efi_uintn_t notify_count;
|
||||
efi_uintn_t handle_count;
|
||||
efi_handle_t *handles;
|
||||
};
|
||||
|
||||
static struct efi_boot_services *boottime;
|
||||
static efi_guid_t guid1 =
|
||||
EFI_GUID(0x2e7ca819, 0x21d3, 0x0a3a,
|
||||
0xf7, 0x91, 0x82, 0x1f, 0x7a, 0x83, 0x67, 0xaf);
|
||||
static efi_guid_t guid2 =
|
||||
EFI_GUID(0xf909f2bb, 0x90a8, 0x0d77,
|
||||
0x94, 0x0c, 0x3e, 0xa8, 0xea, 0x38, 0xd6, 0x6f);
|
||||
static struct context context;
|
||||
static struct efi_event *event;
|
||||
|
||||
/*
|
||||
* Notification function, increments the notification count if parameter
|
||||
* context is provided.
|
||||
*
|
||||
* @event notified event
|
||||
* @context pointer to the notification count
|
||||
*/
|
||||
static void EFIAPI notify(struct efi_event *event, void *context)
|
||||
{
|
||||
struct context *cp = context;
|
||||
efi_status_t ret;
|
||||
efi_uintn_t handle_count;
|
||||
efi_handle_t *handles;
|
||||
|
||||
cp->notify_count++;
|
||||
|
||||
for (;;) {
|
||||
ret = boottime->locate_handle_buffer(BY_REGISTER_NOTIFY, NULL,
|
||||
cp->registration_key,
|
||||
&handle_count, &handles);
|
||||
if (ret != EFI_SUCCESS)
|
||||
break;
|
||||
cp->handle_count += handle_count;
|
||||
cp->handles = handles;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup unit test.
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
*/
|
||||
static int setup(const efi_handle_t img_handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
efi_status_t ret;
|
||||
|
||||
boottime = systable->boottime;
|
||||
|
||||
ret = boottime->create_event(EVT_NOTIFY_SIGNAL,
|
||||
TPL_CALLBACK, notify, &context,
|
||||
&event);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("could not create event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
ret = boottime->register_protocol_notify(&guid1, event,
|
||||
&context.registration_key);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("could not register event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tear down unit test.
|
||||
*
|
||||
*/
|
||||
static int teardown(void)
|
||||
{
|
||||
efi_status_t ret;
|
||||
|
||||
if (event) {
|
||||
ret = boottime->close_event(event);
|
||||
event = NULL;
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("could not close event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute unit test.
|
||||
*
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
efi_status_t ret;
|
||||
efi_handle_t handle1 = NULL, handle2 = NULL;
|
||||
struct interface interface1, interface2;
|
||||
|
||||
ret = boottime->install_protocol_interface(&handle1, &guid1,
|
||||
EFI_NATIVE_INTERFACE,
|
||||
&interface1);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("could not install interface\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (!context.notify_count) {
|
||||
efi_st_error("install was not notified\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (context.notify_count > 1) {
|
||||
efi_st_error("install was notified too often\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (context.handle_count != 1) {
|
||||
efi_st_error("LocateHandle failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->free_pool(context.handles);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("FreePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
context.notify_count = 0;
|
||||
ret = boottime->install_protocol_interface(&handle1, &guid2,
|
||||
EFI_NATIVE_INTERFACE,
|
||||
&interface1);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("could not install interface\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (context.notify_count) {
|
||||
efi_st_error("wrong protocol was notified\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
context.notify_count = 0;
|
||||
ret = boottime->reinstall_protocol_interface(handle1, &guid1,
|
||||
&interface1, &interface2);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("could not reinstall interface\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (!context.notify_count) {
|
||||
efi_st_error("reinstall was not notified\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (context.notify_count > 1) {
|
||||
efi_st_error("reinstall was notified too often\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (context.handle_count != 2) {
|
||||
efi_st_error("LocateHandle failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->free_pool(context.handles);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("FreePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
context.notify_count = 0;
|
||||
ret = boottime->install_protocol_interface(&handle2, &guid1,
|
||||
EFI_NATIVE_INTERFACE,
|
||||
&interface1);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("could not install interface\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (!context.notify_count) {
|
||||
efi_st_error("install was not notified\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (context.notify_count > 1) {
|
||||
efi_st_error("install was notified too often\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (context.handle_count != 3) {
|
||||
efi_st_error("LocateHandle failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->free_pool(context.handles);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("FreePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
ret = boottime->uninstall_multiple_protocol_interfaces
|
||||
(handle1, &guid1, &interface2,
|
||||
&guid2, &interface1, NULL);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("UninstallMultipleProtocolInterfaces failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->uninstall_multiple_protocol_interfaces
|
||||
(handle2, &guid1, &interface1, NULL);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("UninstallMultipleProtocolInterfaces failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(regprotnot) = {
|
||||
.name = "register protocol notify",
|
||||
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup,
|
||||
.execute = execute,
|
||||
.teardown = teardown,
|
||||
};
|
||||
@@ -0,0 +1,102 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_rtc
|
||||
*
|
||||
* Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* Test the real time clock runtime services.
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
|
||||
#define EFI_ST_NO_RTC "Could not read real time clock\n"
|
||||
#define EFI_ST_NO_RTC_SET "Could not set real time clock\n"
|
||||
|
||||
static struct efi_runtime_services *runtime;
|
||||
|
||||
/*
|
||||
* Setup unit test.
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int setup(const efi_handle_t handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
runtime = systable->runtime;
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute unit test.
|
||||
*
|
||||
* Read and display current time.
|
||||
* Set a new value and read it back.
|
||||
* Set the real time clock back the current time.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
efi_status_t ret;
|
||||
struct efi_time tm_old;
|
||||
#ifdef CONFIG_EFI_SET_TIME
|
||||
struct efi_time tm, tm_new = {
|
||||
.year = 2017,
|
||||
.month = 5,
|
||||
.day = 19,
|
||||
.hour = 13,
|
||||
.minute = 47,
|
||||
.second = 53,
|
||||
};
|
||||
#endif
|
||||
|
||||
/* Display current time */
|
||||
ret = runtime->get_time(&tm_old, NULL);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error(EFI_ST_NO_RTC);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
efi_st_printf("Time according to real time clock: "
|
||||
"%.4u-%.2u-%.2u %.2u:%.2u:%.2u\n",
|
||||
tm_old.year, tm_old.month, tm_old.day,
|
||||
tm_old.hour, tm_old.minute, tm_old.second);
|
||||
#ifdef CONFIG_EFI_SET_TIME
|
||||
ret = runtime->set_time(&tm_new);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error(EFI_ST_NO_RTC_SET);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = runtime->get_time(&tm, NULL);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error(EFI_ST_NO_RTC);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (tm.year != tm_new.year ||
|
||||
tm.month != tm_new.month ||
|
||||
tm.day != tm_new.day ||
|
||||
tm.hour != tm_new.hour ||
|
||||
tm.minute != tm_new.minute ||
|
||||
tm.second < tm_new.second ||
|
||||
tm.second > tm_new.second + 2) {
|
||||
efi_st_error(EFI_ST_NO_RTC_SET);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Set time back to old value */
|
||||
ret = runtime->set_time(&tm_old);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error(EFI_ST_NO_RTC_SET);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(rtc) = {
|
||||
.name = "real time clock",
|
||||
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup,
|
||||
.execute = execute,
|
||||
};
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_set_virtual_address_map.c
|
||||
*
|
||||
* Copyright (c) 2019 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* This test checks the notification of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
|
||||
* and the following services: SetVirtualAddressMap, ConvertPointer.
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
|
||||
static const struct efi_boot_services *boottime;
|
||||
static const struct efi_runtime_services *runtime;
|
||||
static struct efi_event *event;
|
||||
static struct efi_mem_desc *memory_map;
|
||||
static efi_uintn_t map_size;
|
||||
static efi_uintn_t desc_size;
|
||||
static u32 desc_version;
|
||||
static u64 page1;
|
||||
static u64 page2;
|
||||
static u32 notify_call_count;
|
||||
static bool convert_pointer_failed;
|
||||
|
||||
/**
|
||||
* notify () - notification function
|
||||
*
|
||||
* This function is called when the EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event
|
||||
* occurs. The correct output of ConvertPointer() is checked.
|
||||
*
|
||||
* @event notified event
|
||||
* @context pointer to the notification count
|
||||
*/
|
||||
static void EFIAPI notify(struct efi_event *event, void *context)
|
||||
{
|
||||
void *addr;
|
||||
efi_status_t ret;
|
||||
|
||||
++notify_call_count;
|
||||
|
||||
addr = (void *)(uintptr_t)page1;
|
||||
ret = runtime->convert_pointer(0, &addr);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("ConvertPointer failed\n");
|
||||
convert_pointer_failed = true;
|
||||
return;
|
||||
}
|
||||
if ((uintptr_t)addr != page1 + EFI_PAGE_SIZE) {
|
||||
efi_st_error("ConvertPointer wrong address\n");
|
||||
convert_pointer_failed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
addr = (void *)(uintptr_t)page2;
|
||||
ret = runtime->convert_pointer(0, &addr);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("ConvertPointer failed\n");
|
||||
convert_pointer_failed = true;
|
||||
return;
|
||||
}
|
||||
if ((uintptr_t)addr != page2 + 2 * EFI_PAGE_SIZE) {
|
||||
efi_st_error("ConvertPointer wrong address\n");
|
||||
convert_pointer_failed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* setup() - setup unit test
|
||||
*
|
||||
* The memory map is read. Boottime only entries are deleted. Two entries for
|
||||
* newly allocated pages are added. For these virtual addresses deviating from
|
||||
* the physical addresses are set.
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int setup(const efi_handle_t handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
efi_uintn_t map_key;
|
||||
efi_status_t ret;
|
||||
struct efi_mem_desc *end, *pos1, *pos2;
|
||||
|
||||
boottime = systable->boottime;
|
||||
runtime = systable->runtime;
|
||||
|
||||
ret = boottime->create_event(EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,
|
||||
TPL_CALLBACK, notify, NULL,
|
||||
&event);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("could not create event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
ret = boottime->get_memory_map(&map_size, NULL, &map_key, &desc_size,
|
||||
&desc_version);
|
||||
if (ret != EFI_BUFFER_TOO_SMALL) {
|
||||
efi_st_error(
|
||||
"GetMemoryMap did not return EFI_BUFFER_TOO_SMALL\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Allocate extra space for newly allocated memory */
|
||||
map_size += 3 * sizeof(struct efi_mem_desc);
|
||||
ret = boottime->allocate_pool(EFI_BOOT_SERVICES_DATA, map_size,
|
||||
(void **)&memory_map);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("AllocatePool failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->get_memory_map(&map_size, memory_map, &map_key,
|
||||
&desc_size, &desc_version);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("GetMemoryMap failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->allocate_pages(EFI_ALLOCATE_ANY_PAGES,
|
||||
EFI_BOOT_SERVICES_DATA, 2, &page1);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("AllocatePages failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->allocate_pages(EFI_ALLOCATE_ANY_PAGES,
|
||||
EFI_BOOT_SERVICES_DATA, 3, &page2);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("AllocatePages failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Remove entries not relevant for runtime from map */
|
||||
end = (struct efi_mem_desc *)((u8 *)memory_map + map_size);
|
||||
for (pos1 = memory_map, pos2 = memory_map;
|
||||
pos2 < end; ++pos2) {
|
||||
switch (pos2->type) {
|
||||
case EFI_LOADER_CODE:
|
||||
case EFI_LOADER_DATA:
|
||||
case EFI_BOOT_SERVICES_CODE:
|
||||
case EFI_BOOT_SERVICES_DATA:
|
||||
case EFI_CONVENTIONAL_MEMORY:
|
||||
continue;
|
||||
}
|
||||
memcpy(pos1, pos2, desc_size);
|
||||
++pos1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add entries with virtual addresses deviating from the physical
|
||||
* addresses. By choosing virtual address ranges within the allocated
|
||||
* physical pages address space collisions are avoided.
|
||||
*/
|
||||
pos1->type = EFI_RUNTIME_SERVICES_DATA;
|
||||
pos1->reserved = 0;
|
||||
pos1->physical_start = page1;
|
||||
pos1->virtual_start = page1 + EFI_PAGE_SIZE;
|
||||
pos1->num_pages = 1;
|
||||
pos1->attribute = EFI_MEMORY_RUNTIME;
|
||||
++pos1;
|
||||
|
||||
pos1->type = EFI_RUNTIME_SERVICES_DATA;
|
||||
pos1->reserved = 0;
|
||||
pos1->physical_start = page2;
|
||||
pos1->virtual_start = page2 + 2 * EFI_PAGE_SIZE;
|
||||
pos1->num_pages = 1;
|
||||
pos1->attribute = EFI_MEMORY_RUNTIME;
|
||||
++pos1;
|
||||
|
||||
map_size = (u8 *)pos1 - (u8 *)memory_map;
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* execute() - execute unit test
|
||||
*
|
||||
* SetVirtualAddressMap() is called with the memory map prepared in setup().
|
||||
*
|
||||
* The triggering of the EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event is checked via
|
||||
* the call count of the notification function.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
efi_status_t ret;
|
||||
|
||||
ret = runtime->set_virtual_address_map(map_size, desc_size,
|
||||
desc_version, memory_map);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("SetVirtualAddressMap failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (notify_call_count != 1) {
|
||||
efi_st_error("EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE triggered %d times\n",
|
||||
notify_call_count);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (convert_pointer_failed)
|
||||
return EFI_ST_FAILURE;
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(virtaddrmap) = {
|
||||
.name = "virtual address map",
|
||||
.phase = EFI_SETUP_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup,
|
||||
.execute = execute,
|
||||
};
|
||||
@@ -0,0 +1,485 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_snp
|
||||
*
|
||||
* Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* This unit test covers the Simple Network Protocol as well as
|
||||
* the CopyMem and SetMem boottime services.
|
||||
*
|
||||
* A DHCP discover message is sent. The test is successful if a
|
||||
* DHCP reply is received.
|
||||
*
|
||||
* TODO: Once ConnectController and DisconnectController are implemented
|
||||
* we should connect our code as controller.
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
|
||||
/*
|
||||
* MAC address for broadcasts
|
||||
*/
|
||||
static const u8 BROADCAST_MAC[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
|
||||
|
||||
struct dhcp_hdr {
|
||||
u8 op;
|
||||
#define BOOTREQUEST 1
|
||||
#define BOOTREPLY 2
|
||||
u8 htype;
|
||||
# define HWT_ETHER 1
|
||||
u8 hlen;
|
||||
# define HWL_ETHER 6
|
||||
u8 hops;
|
||||
u32 xid;
|
||||
u16 secs;
|
||||
u16 flags;
|
||||
#define DHCP_FLAGS_UNICAST 0x0000
|
||||
#define DHCP_FLAGS_BROADCAST 0x0080
|
||||
u32 ciaddr;
|
||||
u32 yiaddr;
|
||||
u32 siaddr;
|
||||
u32 giaddr;
|
||||
u8 chaddr[16];
|
||||
u8 sname[64];
|
||||
u8 file[128];
|
||||
};
|
||||
|
||||
/*
|
||||
* Message type option.
|
||||
*/
|
||||
#define DHCP_MESSAGE_TYPE 0x35
|
||||
#define DHCPDISCOVER 1
|
||||
#define DHCPOFFER 2
|
||||
#define DHCPREQUEST 3
|
||||
#define DHCPDECLINE 4
|
||||
#define DHCPACK 5
|
||||
#define DHCPNAK 6
|
||||
#define DHCPRELEASE 7
|
||||
|
||||
struct dhcp {
|
||||
struct ethernet_hdr eth_hdr;
|
||||
struct ip_udp_hdr ip_udp;
|
||||
struct dhcp_hdr dhcp_hdr;
|
||||
u8 opt[128];
|
||||
} __packed;
|
||||
|
||||
static struct efi_boot_services *boottime;
|
||||
static struct efi_simple_network *net;
|
||||
static struct efi_event *timer;
|
||||
static const efi_guid_t efi_net_guid = EFI_SIMPLE_NETWORK_PROTOCOL_GUID;
|
||||
/* IP packet ID */
|
||||
static unsigned int net_ip_id;
|
||||
|
||||
/*
|
||||
* Compute the checksum of the IP header. We cover even values of length only.
|
||||
* We cannot use net/checksum.c due to different CFLAGS values.
|
||||
*
|
||||
* @buf: IP header
|
||||
* @len: length of header in bytes
|
||||
* @return: checksum
|
||||
*/
|
||||
static unsigned int efi_ip_checksum(const void *buf, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
u32 sum = 0;
|
||||
const u16 *pos = buf;
|
||||
|
||||
for (i = 0; i < len; i += 2)
|
||||
sum += *pos++;
|
||||
|
||||
sum = (sum >> 16) + (sum & 0xffff);
|
||||
sum += sum >> 16;
|
||||
sum = ~sum & 0xffff;
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
/*
|
||||
* Transmit a DHCPDISCOVER message.
|
||||
*/
|
||||
static efi_status_t send_dhcp_discover(void)
|
||||
{
|
||||
efi_status_t ret;
|
||||
struct dhcp p = {};
|
||||
|
||||
/*
|
||||
* Fill Ethernet header
|
||||
*/
|
||||
boottime->copy_mem(p.eth_hdr.et_dest, (void *)BROADCAST_MAC, ARP_HLEN);
|
||||
boottime->copy_mem(p.eth_hdr.et_src, &net->mode->current_address,
|
||||
ARP_HLEN);
|
||||
p.eth_hdr.et_protlen = htons(PROT_IP);
|
||||
/*
|
||||
* Fill IP header
|
||||
*/
|
||||
p.ip_udp.ip_hl_v = 0x45;
|
||||
p.ip_udp.ip_len = htons(sizeof(struct dhcp) -
|
||||
sizeof(struct ethernet_hdr));
|
||||
p.ip_udp.ip_id = htons(++net_ip_id);
|
||||
p.ip_udp.ip_off = htons(IP_FLAGS_DFRAG);
|
||||
p.ip_udp.ip_ttl = 0xff; /* time to live */
|
||||
p.ip_udp.ip_p = IPPROTO_UDP;
|
||||
boottime->set_mem(&p.ip_udp.ip_dst, 4, 0xff);
|
||||
p.ip_udp.ip_sum = efi_ip_checksum(&p.ip_udp, IP_HDR_SIZE);
|
||||
|
||||
/*
|
||||
* Fill UDP header
|
||||
*/
|
||||
p.ip_udp.udp_src = htons(68);
|
||||
p.ip_udp.udp_dst = htons(67);
|
||||
p.ip_udp.udp_len = htons(sizeof(struct dhcp) -
|
||||
sizeof(struct ethernet_hdr) -
|
||||
sizeof(struct ip_hdr));
|
||||
/*
|
||||
* Fill DHCP header
|
||||
*/
|
||||
p.dhcp_hdr.op = BOOTREQUEST;
|
||||
p.dhcp_hdr.htype = HWT_ETHER;
|
||||
p.dhcp_hdr.hlen = HWL_ETHER;
|
||||
p.dhcp_hdr.flags = htons(DHCP_FLAGS_UNICAST);
|
||||
boottime->copy_mem(&p.dhcp_hdr.chaddr,
|
||||
&net->mode->current_address, ARP_HLEN);
|
||||
/*
|
||||
* Fill options
|
||||
*/
|
||||
p.opt[0] = 0x63; /* DHCP magic cookie */
|
||||
p.opt[1] = 0x82;
|
||||
p.opt[2] = 0x53;
|
||||
p.opt[3] = 0x63;
|
||||
p.opt[4] = DHCP_MESSAGE_TYPE;
|
||||
p.opt[5] = 0x01; /* length */
|
||||
p.opt[6] = DHCPDISCOVER;
|
||||
p.opt[7] = 0x39; /* maximum message size */
|
||||
p.opt[8] = 0x02; /* length */
|
||||
p.opt[9] = 0x02; /* 576 bytes */
|
||||
p.opt[10] = 0x40;
|
||||
p.opt[11] = 0xff; /* end of options */
|
||||
|
||||
/*
|
||||
* Transmit DHCPDISCOVER message.
|
||||
*/
|
||||
ret = net->transmit(net, 0, sizeof(struct dhcp), &p, NULL, NULL, 0);
|
||||
if (ret != EFI_SUCCESS)
|
||||
efi_st_error("Sending a DHCP request failed\n");
|
||||
else
|
||||
efi_st_printf("DHCP Discover\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup unit test.
|
||||
*
|
||||
* Create a 1 s periodic timer.
|
||||
* Start the network driver.
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int setup(const efi_handle_t handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
efi_status_t ret;
|
||||
|
||||
boottime = systable->boottime;
|
||||
|
||||
/*
|
||||
* Create a timer event.
|
||||
*/
|
||||
ret = boottime->create_event(EVT_TIMER, TPL_CALLBACK, NULL, NULL,
|
||||
&timer);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to create event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/*
|
||||
* Set timer period to 1s.
|
||||
*/
|
||||
ret = boottime->set_timer(timer, EFI_TIMER_PERIODIC, 10000000);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to set timer\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/*
|
||||
* Find an interface implementing the SNP protocol.
|
||||
*/
|
||||
ret = boottime->locate_protocol(&efi_net_guid, NULL, (void **)&net);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
net = NULL;
|
||||
efi_st_error("Failed to locate simple network protocol\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/*
|
||||
* Check hardware address size.
|
||||
*/
|
||||
if (!net->mode) {
|
||||
efi_st_error("Mode not provided\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (net->mode->hwaddr_size != ARP_HLEN) {
|
||||
efi_st_error("HwAddressSize = %u, expected %u\n",
|
||||
net->mode->hwaddr_size, ARP_HLEN);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/*
|
||||
* Check that WaitForPacket event exists.
|
||||
*/
|
||||
if (!net->wait_for_packet) {
|
||||
efi_st_error("WaitForPacket event missing\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (net->mode->state == EFI_NETWORK_INITIALIZED) {
|
||||
/*
|
||||
* Shut down network adapter.
|
||||
*/
|
||||
ret = net->shutdown(net);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to shut down network adapter\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
}
|
||||
if (net->mode->state == EFI_NETWORK_STARTED) {
|
||||
/*
|
||||
* Stop network adapter.
|
||||
*/
|
||||
ret = net->stop(net);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to stop network adapter\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Start network adapter.
|
||||
*/
|
||||
ret = net->start(net);
|
||||
if (ret != EFI_SUCCESS && ret != EFI_ALREADY_STARTED) {
|
||||
efi_st_error("Failed to start network adapter\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (net->mode->state != EFI_NETWORK_STARTED) {
|
||||
efi_st_error("Failed to start network adapter\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/*
|
||||
* Initialize network adapter.
|
||||
*/
|
||||
ret = net->initialize(net, 0, 0);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to initialize network adapter\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (net->mode->state != EFI_NETWORK_INITIALIZED) {
|
||||
efi_st_error("Failed to initialize network adapter\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute unit test.
|
||||
*
|
||||
* A DHCP discover message is sent. The test is successful if a
|
||||
* DHCP reply is received within 10 seconds.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
efi_status_t ret;
|
||||
struct efi_event *events[2];
|
||||
efi_uintn_t index;
|
||||
union {
|
||||
struct dhcp p;
|
||||
u8 b[PKTSIZE];
|
||||
} buffer;
|
||||
struct efi_mac_address srcaddr;
|
||||
struct efi_mac_address destaddr;
|
||||
size_t buffer_size;
|
||||
u8 *addr;
|
||||
|
||||
/*
|
||||
* The timeout is to occur after 10 s.
|
||||
*/
|
||||
unsigned int timeout = 10;
|
||||
|
||||
/* Setup may have failed */
|
||||
if (!net || !timer) {
|
||||
efi_st_error("Cannot execute test after setup failure\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send DHCP discover message
|
||||
*/
|
||||
ret = send_dhcp_discover();
|
||||
if (ret != EFI_SUCCESS)
|
||||
return EFI_ST_FAILURE;
|
||||
|
||||
/*
|
||||
* If we would call WaitForEvent only with the WaitForPacket event,
|
||||
* our code would block until a packet is received which might never
|
||||
* occur. By calling WaitFor event with both a timer event and the
|
||||
* WaitForPacket event we can escape this blocking situation.
|
||||
*
|
||||
* If the timer event occurs before we have received a DHCP reply
|
||||
* a further DHCP discover message is sent.
|
||||
*/
|
||||
events[0] = timer;
|
||||
events[1] = net->wait_for_packet;
|
||||
for (;;) {
|
||||
u32 int_status;
|
||||
|
||||
/*
|
||||
* Wait for packet to be received or timer event.
|
||||
*/
|
||||
boottime->wait_for_event(2, events, &index);
|
||||
if (index == 0) {
|
||||
/*
|
||||
* The timer event occurred. Check for timeout.
|
||||
*/
|
||||
--timeout;
|
||||
if (!timeout) {
|
||||
efi_st_error("Timeout occurred\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/*
|
||||
* Send further DHCP discover message
|
||||
*/
|
||||
ret = send_dhcp_discover();
|
||||
if (ret != EFI_SUCCESS)
|
||||
return EFI_ST_FAILURE;
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* Receive packet
|
||||
*/
|
||||
buffer_size = sizeof(buffer);
|
||||
ret = net->get_status(net, &int_status, NULL);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to get status");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (!(int_status & EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT)) {
|
||||
efi_st_error("RX interrupt not set");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = net->receive(net, NULL, &buffer_size, &buffer,
|
||||
&srcaddr, &destaddr, NULL);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to receive packet");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/*
|
||||
* Check the packet is meant for this system.
|
||||
* Unfortunately QEMU ignores the broadcast flag.
|
||||
* So we have to check for broadcasts too.
|
||||
*/
|
||||
if (memcmp(&destaddr, &net->mode->current_address, ARP_HLEN) &&
|
||||
memcmp(&destaddr, BROADCAST_MAC, ARP_HLEN))
|
||||
continue;
|
||||
/*
|
||||
* Check this is a DHCP reply
|
||||
*/
|
||||
if (buffer.p.eth_hdr.et_protlen != ntohs(PROT_IP) ||
|
||||
buffer.p.ip_udp.ip_hl_v != 0x45 ||
|
||||
buffer.p.ip_udp.ip_p != IPPROTO_UDP ||
|
||||
buffer.p.ip_udp.udp_src != ntohs(67) ||
|
||||
buffer.p.ip_udp.udp_dst != ntohs(68) ||
|
||||
buffer.p.dhcp_hdr.op != BOOTREPLY)
|
||||
continue;
|
||||
/*
|
||||
* We successfully received a DHCP reply.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a log message.
|
||||
*/
|
||||
addr = (u8 *)&buffer.p.ip_udp.ip_src;
|
||||
efi_st_printf("DHCP reply received from %u.%u.%u.%u (%pm) ",
|
||||
addr[0], addr[1], addr[2], addr[3], &srcaddr);
|
||||
if (!memcmp(&destaddr, BROADCAST_MAC, ARP_HLEN))
|
||||
efi_st_printf("as broadcast message.\n");
|
||||
else
|
||||
efi_st_printf("as unicast message.\n");
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tear down unit test.
|
||||
*
|
||||
* Close the timer event created in setup.
|
||||
* Shut down the network adapter.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int teardown(void)
|
||||
{
|
||||
efi_status_t ret;
|
||||
int exit_status = EFI_ST_SUCCESS;
|
||||
|
||||
if (timer) {
|
||||
/*
|
||||
* Stop timer.
|
||||
*/
|
||||
ret = boottime->set_timer(timer, EFI_TIMER_STOP, 0);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to stop timer");
|
||||
exit_status = EFI_ST_FAILURE;
|
||||
}
|
||||
/*
|
||||
* Close timer event.
|
||||
*/
|
||||
ret = boottime->close_event(timer);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to close event");
|
||||
exit_status = EFI_ST_FAILURE;
|
||||
}
|
||||
}
|
||||
if (net) {
|
||||
/*
|
||||
* Shut down network adapter.
|
||||
*/
|
||||
ret = net->shutdown(net);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to shut down network adapter\n");
|
||||
exit_status = EFI_ST_FAILURE;
|
||||
}
|
||||
if (net->mode->state != EFI_NETWORK_STARTED) {
|
||||
efi_st_error("Failed to shutdown network adapter\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/*
|
||||
* Stop network adapter.
|
||||
*/
|
||||
ret = net->stop(net);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to stop network adapter\n");
|
||||
exit_status = EFI_ST_FAILURE;
|
||||
}
|
||||
if (net->mode->state != EFI_NETWORK_STOPPED) {
|
||||
efi_st_error("Failed to stop network adapter\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return exit_status;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(snp) = {
|
||||
.name = "simple network protocol",
|
||||
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup,
|
||||
.execute = execute,
|
||||
.teardown = teardown,
|
||||
#ifdef CONFIG_SANDBOX
|
||||
/*
|
||||
* Running this test on the sandbox requires setting environment
|
||||
* variable ethact to a network interface connected to a DHCP server and
|
||||
* ethrotate to 'no'.
|
||||
*/
|
||||
.on_request = true,
|
||||
#endif
|
||||
};
|
||||
@@ -0,0 +1,161 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_start_image
|
||||
*
|
||||
* Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* This test checks the StartImage boot service.
|
||||
* The efi_selftest_miniapp_exit.efi application is loaded into memory
|
||||
* and started.
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
/* Include containing the miniapp.efi application */
|
||||
#include "efi_miniapp_file_image_exit.h"
|
||||
|
||||
/* Block size of compressed disk image */
|
||||
#define COMPRESSED_DISK_IMAGE_BLOCK_SIZE 8
|
||||
|
||||
/* Binary logarithm of the block size */
|
||||
#define LB_BLOCK_SIZE 9
|
||||
|
||||
static efi_handle_t image_handle;
|
||||
static struct efi_boot_services *boottime;
|
||||
|
||||
/* One 8 byte block of the compressed disk image */
|
||||
struct line {
|
||||
size_t addr;
|
||||
char *line;
|
||||
};
|
||||
|
||||
/* Compressed file image */
|
||||
struct compressed_file_image {
|
||||
size_t length;
|
||||
struct line lines[];
|
||||
};
|
||||
|
||||
static struct compressed_file_image img = EFI_ST_DISK_IMG;
|
||||
|
||||
/* Decompressed file image */
|
||||
static u8 *image;
|
||||
|
||||
/*
|
||||
* Decompress the disk image.
|
||||
*
|
||||
* @image decompressed disk image
|
||||
* @return status code
|
||||
*/
|
||||
static efi_status_t decompress(u8 **image)
|
||||
{
|
||||
u8 *buf;
|
||||
size_t i;
|
||||
size_t addr;
|
||||
size_t len;
|
||||
efi_status_t ret;
|
||||
|
||||
ret = boottime->allocate_pool(EFI_LOADER_DATA, img.length,
|
||||
(void **)&buf);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Out of memory\n");
|
||||
return ret;
|
||||
}
|
||||
boottime->set_mem(buf, img.length, 0);
|
||||
|
||||
for (i = 0; ; ++i) {
|
||||
if (!img.lines[i].line)
|
||||
break;
|
||||
addr = img.lines[i].addr;
|
||||
len = COMPRESSED_DISK_IMAGE_BLOCK_SIZE;
|
||||
if (addr + len > img.length)
|
||||
len = img.length - addr;
|
||||
boottime->copy_mem(buf + addr, img.lines[i].line, len);
|
||||
}
|
||||
*image = buf;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup unit test.
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int setup(const efi_handle_t handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
image_handle = handle;
|
||||
boottime = systable->boottime;
|
||||
|
||||
/* Load the application image into memory */
|
||||
decompress(&image);
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tear down unit test.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int teardown(void)
|
||||
{
|
||||
efi_status_t r = EFI_ST_SUCCESS;
|
||||
|
||||
if (image) {
|
||||
r = boottime->free_pool(image);
|
||||
if (r != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to free image\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute unit test.
|
||||
*
|
||||
* Load and start the application image.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
efi_status_t ret;
|
||||
efi_handle_t handle;
|
||||
efi_uintn_t exit_data_size = 0;
|
||||
u16 *exit_data = NULL;
|
||||
u16 expected_text[] = EFI_ST_SUCCESS_STR;
|
||||
|
||||
ret = boottime->load_image(false, image_handle, NULL, image,
|
||||
img.length, &handle);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to load image\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->start_image(handle, &exit_data_size, &exit_data);
|
||||
if (ret != EFI_UNSUPPORTED) {
|
||||
efi_st_error("Wrong return value from application\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (!exit_data || exit_data_size != sizeof(expected_text) ||
|
||||
memcmp(exit_data, expected_text, sizeof(expected_text))) {
|
||||
efi_st_error("Incorrect exit data\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->free_pool(exit_data);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to free exit data\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(startimage_exit) = {
|
||||
.name = "start image exit",
|
||||
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup,
|
||||
.execute = execute,
|
||||
.teardown = teardown,
|
||||
};
|
||||
@@ -0,0 +1,148 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_start_image
|
||||
*
|
||||
* Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* This test checks the StartImage boot service.
|
||||
* The efi_selftest_miniapp_return.efi application is loaded into memory
|
||||
* and started.
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
/* Include containing the miniapp.efi application */
|
||||
#include "efi_miniapp_file_image_return.h"
|
||||
|
||||
/* Block size of compressed disk image */
|
||||
#define COMPRESSED_DISK_IMAGE_BLOCK_SIZE 8
|
||||
|
||||
/* Binary logarithm of the block size */
|
||||
#define LB_BLOCK_SIZE 9
|
||||
|
||||
static efi_handle_t image_handle;
|
||||
static struct efi_boot_services *boottime;
|
||||
|
||||
/* One 8 byte block of the compressed disk image */
|
||||
struct line {
|
||||
size_t addr;
|
||||
char *line;
|
||||
};
|
||||
|
||||
/* Compressed file image */
|
||||
struct compressed_file_image {
|
||||
size_t length;
|
||||
struct line lines[];
|
||||
};
|
||||
|
||||
static struct compressed_file_image img = EFI_ST_DISK_IMG;
|
||||
|
||||
/* Decompressed file image */
|
||||
static u8 *image;
|
||||
|
||||
/*
|
||||
* Decompress the disk image.
|
||||
*
|
||||
* @image decompressed disk image
|
||||
* @return status code
|
||||
*/
|
||||
static efi_status_t decompress(u8 **image)
|
||||
{
|
||||
u8 *buf;
|
||||
size_t i;
|
||||
size_t addr;
|
||||
size_t len;
|
||||
efi_status_t ret;
|
||||
|
||||
ret = boottime->allocate_pool(EFI_LOADER_DATA, img.length,
|
||||
(void **)&buf);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Out of memory\n");
|
||||
return ret;
|
||||
}
|
||||
boottime->set_mem(buf, img.length, 0);
|
||||
|
||||
for (i = 0; ; ++i) {
|
||||
if (!img.lines[i].line)
|
||||
break;
|
||||
addr = img.lines[i].addr;
|
||||
len = COMPRESSED_DISK_IMAGE_BLOCK_SIZE;
|
||||
if (addr + len > img.length)
|
||||
len = img.length - addr;
|
||||
boottime->copy_mem(buf + addr, img.lines[i].line, len);
|
||||
}
|
||||
*image = buf;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup unit test.
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int setup(const efi_handle_t handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
image_handle = handle;
|
||||
boottime = systable->boottime;
|
||||
|
||||
/* Load the application image into memory */
|
||||
decompress(&image);
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tear down unit test.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int teardown(void)
|
||||
{
|
||||
efi_status_t r = EFI_ST_SUCCESS;
|
||||
|
||||
if (image) {
|
||||
r = boottime->free_pool(image);
|
||||
if (r != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to free image\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute unit test.
|
||||
*
|
||||
* Load and start the application image.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
efi_status_t ret;
|
||||
efi_handle_t handle;
|
||||
|
||||
ret = boottime->load_image(false, image_handle, NULL, image,
|
||||
img.length, &handle);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Failed to load image\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->start_image(handle, NULL, NULL);
|
||||
if (ret != EFI_INCOMPATIBLE_VERSION) {
|
||||
efi_st_error("Wrong return value from application\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(startimage) = {
|
||||
.name = "start image return",
|
||||
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup,
|
||||
.execute = execute,
|
||||
.teardown = teardown,
|
||||
};
|
||||
@@ -0,0 +1,97 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_textinput
|
||||
*
|
||||
* Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* Provides a unit test for the EFI_SIMPLE_TEXT_INPUT_PROTOCOL.
|
||||
* The Unicode character and the scan code are printed for text
|
||||
* input. To run the test:
|
||||
*
|
||||
* setenv efi_selftest text input
|
||||
* bootefi selftest
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
|
||||
static struct efi_boot_services *boottime;
|
||||
|
||||
/*
|
||||
* Setup unit test.
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int setup(const efi_handle_t handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
boottime = systable->boottime;
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute unit test.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
struct efi_input_key input_key = {0};
|
||||
efi_status_t ret;
|
||||
efi_uintn_t index;
|
||||
|
||||
/* Drain the console input */
|
||||
ret = con_in->reset(con_in, true);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Reset failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = con_in->read_key_stroke(con_in, &input_key);
|
||||
if (ret != EFI_NOT_READY) {
|
||||
efi_st_error("Empty buffer not reported\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
efi_st_printf("Waiting for your input\n");
|
||||
efi_st_printf("To terminate type 'x'\n");
|
||||
|
||||
for (;;) {
|
||||
/* Wait for next key */
|
||||
ret = boottime->wait_for_event(1, &con_in->wait_for_key,
|
||||
&index);
|
||||
if (ret != EFI_ST_SUCCESS) {
|
||||
efi_st_error("WaitForEvent failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = con_in->read_key_stroke(con_in, &input_key);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("ReadKeyStroke failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/* Allow 5 minutes until time out */
|
||||
boottime->set_watchdog_timer(300, 0, 0, NULL);
|
||||
|
||||
efi_st_printf("Unicode char %u (%ps), scan code %u (%ps)\n",
|
||||
(unsigned int)input_key.unicode_char,
|
||||
efi_st_translate_char(input_key.unicode_char),
|
||||
(unsigned int)input_key.scan_code,
|
||||
efi_st_translate_code(input_key.scan_code));
|
||||
|
||||
switch (input_key.unicode_char) {
|
||||
case 'x':
|
||||
case 'X':
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(textinput) = {
|
||||
.name = "text input",
|
||||
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup,
|
||||
.execute = execute,
|
||||
.on_request = true,
|
||||
};
|
||||
@@ -0,0 +1,198 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_textinput
|
||||
*
|
||||
* Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* Provides a unit test for the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
|
||||
* The unicode character and the scan code are printed for text
|
||||
* input. To run the test:
|
||||
*
|
||||
* setenv efi_selftest extended text input
|
||||
* bootefi selftest
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
|
||||
static const efi_guid_t text_input_ex_protocol_guid =
|
||||
EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
|
||||
|
||||
static struct efi_simple_text_input_ex_protocol *con_in_ex;
|
||||
|
||||
static struct efi_boot_services *boottime;
|
||||
|
||||
static void *efi_key_notify_handle;
|
||||
static bool efi_running;
|
||||
|
||||
/**
|
||||
* efi_key_notify_function() - key notification function
|
||||
*
|
||||
* This function is called when the registered key is hit.
|
||||
*
|
||||
* @key_data: next key
|
||||
* Return: status code
|
||||
*/
|
||||
static efi_status_t EFIAPI efi_key_notify_function
|
||||
(struct efi_key_data *key_data)
|
||||
{
|
||||
efi_running = false;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup unit test.
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int setup(const efi_handle_t handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
efi_status_t ret;
|
||||
struct efi_key_data key_data = {
|
||||
.key = {
|
||||
.scan_code = 0,
|
||||
.unicode_char = 0x18
|
||||
},
|
||||
.key_state = {
|
||||
.key_shift_state = EFI_SHIFT_STATE_VALID |
|
||||
EFI_LEFT_CONTROL_PRESSED,
|
||||
.key_toggle_state = EFI_TOGGLE_STATE_INVALID,
|
||||
},
|
||||
};
|
||||
|
||||
boottime = systable->boottime;
|
||||
|
||||
ret = boottime->locate_protocol(&text_input_ex_protocol_guid, NULL,
|
||||
(void **)&con_in_ex);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
con_in_ex = NULL;
|
||||
efi_st_error
|
||||
("Extended text input protocol is not available.\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
ret = con_in_ex->register_key_notify(con_in_ex, &key_data,
|
||||
efi_key_notify_function,
|
||||
&efi_key_notify_handle);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_key_notify_handle = NULL;
|
||||
efi_st_error
|
||||
("Notify function could not be registered.\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
efi_running = true;
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tear down unit test.
|
||||
*
|
||||
* Unregister notify function.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int teardown(void)
|
||||
{
|
||||
efi_status_t ret;
|
||||
|
||||
ret = con_in_ex->unregister_key_notify
|
||||
(con_in_ex, efi_key_notify_handle);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error
|
||||
("Notify function could not be registered.\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
/*
|
||||
* Execute unit test.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
struct efi_key_data input_key = { {0, 0}, {0, 0} };
|
||||
efi_status_t ret;
|
||||
efi_uintn_t index;
|
||||
|
||||
if (!con_in_ex) {
|
||||
efi_st_printf("Setup failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/* Drain the console input */
|
||||
ret = con_in_ex->reset(con_in_ex, true);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Reset failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = con_in_ex->read_key_stroke_ex(con_in_ex, &input_key);
|
||||
if (ret != EFI_NOT_READY) {
|
||||
efi_st_error("Empty buffer not reported\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
efi_st_printf("Waiting for your input\n");
|
||||
efi_st_printf("To terminate type 'CTRL+x'\n");
|
||||
|
||||
while (efi_running) {
|
||||
/* Wait for next key */
|
||||
ret = boottime->wait_for_event(1, &con_in_ex->wait_for_key_ex,
|
||||
&index);
|
||||
if (ret != EFI_ST_SUCCESS) {
|
||||
efi_st_error("WaitForEvent failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = con_in_ex->read_key_stroke_ex(con_in_ex, &input_key);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("ReadKeyStroke failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
/* Allow 5 minutes until time out */
|
||||
boottime->set_watchdog_timer(300, 0, 0, NULL);
|
||||
|
||||
efi_st_printf("Unicode char %u (%ps), scan code %u (",
|
||||
(unsigned int)input_key.key.unicode_char,
|
||||
efi_st_translate_char(input_key.key.unicode_char),
|
||||
(unsigned int)input_key.key.scan_code);
|
||||
if (input_key.key_state.key_shift_state &
|
||||
EFI_SHIFT_STATE_VALID) {
|
||||
if (input_key.key_state.key_shift_state &
|
||||
(EFI_LEFT_SHIFT_PRESSED | EFI_RIGHT_SHIFT_PRESSED))
|
||||
efi_st_printf("SHIFT+");
|
||||
if (input_key.key_state.key_shift_state &
|
||||
(EFI_LEFT_ALT_PRESSED | EFI_RIGHT_ALT_PRESSED))
|
||||
efi_st_printf("ALT+");
|
||||
if (input_key.key_state.key_shift_state &
|
||||
(EFI_LEFT_CONTROL_PRESSED |
|
||||
EFI_RIGHT_CONTROL_PRESSED))
|
||||
efi_st_printf("CTRL+");
|
||||
if (input_key.key_state.key_shift_state &
|
||||
(EFI_LEFT_LOGO_PRESSED | EFI_RIGHT_LOGO_PRESSED))
|
||||
efi_st_printf("META+");
|
||||
if (input_key.key_state.key_shift_state ==
|
||||
EFI_SHIFT_STATE_VALID)
|
||||
efi_st_printf("+");
|
||||
}
|
||||
|
||||
efi_st_printf("%ps)\n",
|
||||
efi_st_translate_code(input_key.key.scan_code));
|
||||
|
||||
}
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(textinputex) = {
|
||||
.name = "extended text input",
|
||||
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup,
|
||||
.execute = execute,
|
||||
.teardown = teardown,
|
||||
.on_request = true,
|
||||
};
|
||||
@@ -0,0 +1,130 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_textoutput
|
||||
*
|
||||
* Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* Test the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.
|
||||
*
|
||||
* The following services are tested:
|
||||
* OutputString, TestString, SetAttribute.
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
|
||||
/*
|
||||
* Execute unit test.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
size_t foreground;
|
||||
size_t background;
|
||||
size_t attrib;
|
||||
efi_status_t ret;
|
||||
s16 col;
|
||||
u16 cr[] = { 0x0d, 0x00 };
|
||||
u16 lf[] = { 0x0a, 0x00 };
|
||||
u16 brahmi[] = { /* 2 Brahmi letters */
|
||||
0xD804, 0xDC05,
|
||||
0xD804, 0xDC22,
|
||||
0};
|
||||
|
||||
/* SetAttribute */
|
||||
efi_st_printf("\nColor palette\n");
|
||||
for (foreground = 0; foreground < 0x10; ++foreground) {
|
||||
for (background = 0; background < 0x80; background += 0x10) {
|
||||
attrib = foreground | background;
|
||||
con_out->set_attribute(con_out, attrib);
|
||||
efi_st_printf("%p", (void *)attrib);
|
||||
}
|
||||
con_out->set_attribute(con_out, 0);
|
||||
efi_st_printf("\n");
|
||||
}
|
||||
/* TestString */
|
||||
ret = con_out->test_string(con_out,
|
||||
L" !\"#$%&'()*+,-./0-9:;<=>?@A-Z[\\]^_`a-z{|}~\n");
|
||||
if (ret != EFI_ST_SUCCESS) {
|
||||
efi_st_error("TestString failed for ANSI characters\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* OutputString */
|
||||
ret = con_out->output_string(con_out,
|
||||
L"Testing cursor column update\n");
|
||||
if (ret != EFI_ST_SUCCESS) {
|
||||
efi_st_error("OutputString failed for ANSI characters");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
col = con_out->mode->cursor_column;
|
||||
ret = con_out->output_string(con_out, lf);
|
||||
if (ret != EFI_ST_SUCCESS) {
|
||||
efi_st_error("OutputString failed for line feed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (con_out->mode->cursor_column != col) {
|
||||
efi_st_error("Cursor column changed by line feed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = con_out->output_string(con_out, cr);
|
||||
if (ret != EFI_ST_SUCCESS) {
|
||||
efi_st_error("OutputString failed for carriage return\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (con_out->mode->cursor_column) {
|
||||
efi_st_error("Cursor column not 0 at beginning of line\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = con_out->output_string(con_out, L"123");
|
||||
if (ret != EFI_ST_SUCCESS) {
|
||||
efi_st_error("OutputString failed for ANSI characters\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (con_out->mode->cursor_column != 3) {
|
||||
efi_st_error("Cursor column not incremented properly\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = con_out->output_string(con_out, L"\b");
|
||||
if (ret != EFI_ST_SUCCESS) {
|
||||
efi_st_error("OutputString failed for backspace\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (con_out->mode->cursor_column != 2) {
|
||||
efi_st_error("Cursor column not decremented properly\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = con_out->output_string(con_out, L"\b\b");
|
||||
if (ret != EFI_ST_SUCCESS) {
|
||||
efi_st_error("OutputString failed for backspace\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (con_out->mode->cursor_column) {
|
||||
efi_st_error("Cursor column not decremented properly\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = con_out->output_string(con_out, L"\b\b");
|
||||
if (ret != EFI_ST_SUCCESS) {
|
||||
efi_st_error("OutputString failed for backspace\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (con_out->mode->cursor_column) {
|
||||
efi_st_error("Cursor column decremented past zero\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = con_out->output_string(con_out, brahmi);
|
||||
if (ret != EFI_ST_SUCCESS) {
|
||||
efi_st_todo("Unicode output not fully supported\n");
|
||||
} else if (con_out->mode->cursor_column != 2) {
|
||||
efi_st_printf("Unicode not handled properly\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
efi_st_printf("\n");
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(textoutput) = {
|
||||
.name = "text output",
|
||||
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
.execute = execute,
|
||||
};
|
||||
@@ -0,0 +1,226 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_tpl
|
||||
*
|
||||
* Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* This unit test uses timer events to check the handling of
|
||||
* task priority levels.
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
|
||||
static struct efi_event *event_notify;
|
||||
static struct efi_event *event_wait;
|
||||
static unsigned int notification_count;
|
||||
static struct efi_boot_services *boottime;
|
||||
|
||||
/*
|
||||
* Notification function, increments the notification count.
|
||||
*
|
||||
* @event notified event
|
||||
* @context pointer to the notification count
|
||||
*/
|
||||
static void EFIAPI notify(struct efi_event *event, void *context)
|
||||
{
|
||||
unsigned int *count = context;
|
||||
|
||||
if (count)
|
||||
++*count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup unit test.
|
||||
*
|
||||
* Create two timer events.
|
||||
* One with EVT_NOTIFY_SIGNAL, the other with EVT_NOTIFY_WAIT.
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int setup(const efi_handle_t handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
efi_status_t ret;
|
||||
|
||||
boottime = systable->boottime;
|
||||
|
||||
ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL,
|
||||
TPL_CALLBACK, notify,
|
||||
(void *)¬ification_count,
|
||||
&event_notify);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("could not create event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_WAIT,
|
||||
TPL_HIGH_LEVEL, notify, NULL, &event_wait);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("could not create event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tear down unit test.
|
||||
*
|
||||
* Close the events created in setup.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int teardown(void)
|
||||
{
|
||||
efi_status_t ret;
|
||||
|
||||
if (event_notify) {
|
||||
ret = boottime->close_event(event_notify);
|
||||
event_notify = NULL;
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("could not close event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
}
|
||||
if (event_wait) {
|
||||
ret = boottime->close_event(event_wait);
|
||||
event_wait = NULL;
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("could not close event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
}
|
||||
boottime->restore_tpl(TPL_APPLICATION);
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute unit test.
|
||||
*
|
||||
* Run a 10 ms periodic timer and check that it is called 10 times
|
||||
* while waiting for 100 ms single shot timer.
|
||||
*
|
||||
* Raise the TPL level to the level of the 10 ms timer and observe
|
||||
* that the notification function is not called again.
|
||||
*
|
||||
* Lower the TPL level and check that the queued notification
|
||||
* function is called.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
efi_uintn_t index;
|
||||
efi_status_t ret;
|
||||
efi_uintn_t old_tpl;
|
||||
|
||||
/* Set 10 ms timer */
|
||||
notification_count = 0;
|
||||
ret = boottime->set_timer(event_notify, EFI_TIMER_PERIODIC, 100000);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Could not set timer\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Set 100 ms timer */
|
||||
ret = boottime->set_timer(event_wait, EFI_TIMER_RELATIVE, 1000000);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Could not set timer\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
index = 5;
|
||||
ret = boottime->wait_for_event(1, &event_wait, &index);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Could not wait for event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->check_event(event_wait);
|
||||
if (ret != EFI_NOT_READY) {
|
||||
efi_st_error("Signaled state was not cleared.\n");
|
||||
efi_st_printf("ret = %u\n", (unsigned int)ret);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (index != 0) {
|
||||
efi_st_error("WaitForEvent returned wrong index\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (notification_count < 8 || notification_count > 12) {
|
||||
efi_st_printf(
|
||||
"Notification count with TPL level TPL_APPLICATION: %u\n",
|
||||
notification_count);
|
||||
efi_st_error("Incorrect timing of events\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->set_timer(event_notify, EFI_TIMER_STOP, 0);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Could not cancel timer\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Raise TPL level */
|
||||
old_tpl = boottime->raise_tpl(TPL_CALLBACK);
|
||||
if (old_tpl != TPL_APPLICATION) {
|
||||
efi_st_error("Initial TPL level was not TPL_APPLICATION");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Set 10 ms timer */
|
||||
notification_count = 0;
|
||||
ret = boottime->set_timer(event_notify, EFI_TIMER_PERIODIC, 100000);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Could not set timer\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Set 100 ms timer */
|
||||
ret = boottime->set_timer(event_wait, EFI_TIMER_RELATIVE, 1000000);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Could not set timer\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
do {
|
||||
ret = boottime->check_event(event_wait);
|
||||
} while (ret == EFI_NOT_READY);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Could not check event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (notification_count != 0) {
|
||||
efi_st_printf(
|
||||
"Notification count with TPL level TPL_CALLBACK: %u\n",
|
||||
notification_count);
|
||||
efi_st_error("Suppressed timer fired\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Set 1 ms timer */
|
||||
ret = boottime->set_timer(event_wait, EFI_TIMER_RELATIVE, 1000);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Could not set timer\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Restore the old TPL level */
|
||||
boottime->restore_tpl(TPL_APPLICATION);
|
||||
ret = boottime->wait_for_event(1, &event_wait, &index);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Could not wait for event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (notification_count < 1) {
|
||||
efi_st_printf(
|
||||
"Notification count with TPL level TPL_APPLICATION: %u\n",
|
||||
notification_count);
|
||||
efi_st_error("Queued timer event did not fire\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->set_timer(event_wait, EFI_TIMER_STOP, 0);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Could not cancel timer\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(tpl) = {
|
||||
.name = "task priority levels",
|
||||
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup,
|
||||
.execute = execute,
|
||||
.teardown = teardown,
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_unaligned
|
||||
*
|
||||
* Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* Test unaligned memory access on ARMv7.
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
|
||||
struct aligned_buffer {
|
||||
char a[8] __aligned(8);
|
||||
};
|
||||
|
||||
/*
|
||||
* Return an u32 at a give address.
|
||||
* If the address is not four byte aligned, an unaligned memory access
|
||||
* occurs.
|
||||
*
|
||||
* @addr: address to read
|
||||
* @return: value at the address
|
||||
*/
|
||||
static inline u32 deref(u32 *addr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
asm(
|
||||
"ldr %[out], [%[in]]\n\t"
|
||||
: [out] "=r" (ret)
|
||||
: [in] "r" (addr)
|
||||
);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute unit test.
|
||||
* An unaligned memory access is executed. The result is checked.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
struct aligned_buffer buf = {
|
||||
{0, 1, 2, 3, 4, 5, 6, 7},
|
||||
};
|
||||
void *v = &buf;
|
||||
u32 r = 0;
|
||||
|
||||
/* Read an unaligned address */
|
||||
r = deref(v + 1);
|
||||
|
||||
/* UEFI only supports low endian systems */
|
||||
if (r != 0x04030201) {
|
||||
efi_st_error("Unaligned access failed");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(unaligned) = {
|
||||
.name = "unaligned memory access",
|
||||
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
.execute = execute,
|
||||
};
|
||||
@@ -0,0 +1,260 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_unicode_collation
|
||||
*
|
||||
* Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* Test unicode collation protocol.
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
|
||||
static const efi_guid_t unicode_collation_protocol_guid =
|
||||
EFI_UNICODE_COLLATION_PROTOCOL2_GUID;
|
||||
|
||||
static struct efi_boot_services *boottime;
|
||||
|
||||
static struct efi_unicode_collation_protocol *unicode_collation_protocol;
|
||||
|
||||
/**
|
||||
* setup() - setup unit test.
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
* ReturnValue: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int setup(const efi_handle_t handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
efi_status_t ret;
|
||||
|
||||
boottime = systable->boottime;
|
||||
|
||||
ret = boottime->locate_protocol(&unicode_collation_protocol_guid, NULL,
|
||||
(void **)&unicode_collation_protocol);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
unicode_collation_protocol = NULL;
|
||||
efi_st_error("Unicode collation protocol is not available.\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
static int test_stri_coll(void)
|
||||
{
|
||||
efi_intn_t ret;
|
||||
u16 c1[] = L"first";
|
||||
u16 c2[] = L"FIRST";
|
||||
u16 c3[] = L"second";
|
||||
|
||||
ret = unicode_collation_protocol->stri_coll(unicode_collation_protocol,
|
||||
c1, c2);
|
||||
if (ret) {
|
||||
efi_st_error(
|
||||
"stri_coll(\"%ps\", \"%ps\") = %d\n", c1, c2, (int)ret);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
ret = unicode_collation_protocol->stri_coll(unicode_collation_protocol,
|
||||
c1, c3);
|
||||
if (ret >= 0) {
|
||||
efi_st_error(
|
||||
"stri_coll(\"%ps\", \"%ps\") = %d\n", c1, c3, (int)ret);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
ret = unicode_collation_protocol->stri_coll(unicode_collation_protocol,
|
||||
c3, c1);
|
||||
if (ret <= 0) {
|
||||
efi_st_error(
|
||||
"stri_coll(\"%ps\", \"%ps\") = %d\n", c3, c1, (int)ret);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
static int test_metai_match(void)
|
||||
{
|
||||
bool ret;
|
||||
const u16 c[] = L"Das U-Boot";
|
||||
|
||||
ret = unicode_collation_protocol->metai_match(
|
||||
unicode_collation_protocol, c, L"*");
|
||||
if (!ret) {
|
||||
efi_st_error("metai_match returned %u\n", ret);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
ret = unicode_collation_protocol->metai_match(
|
||||
unicode_collation_protocol, c, L"Da[rstu] U-Boot");
|
||||
if (!ret) {
|
||||
efi_st_error("metai_match returned %u\n", ret);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
ret = unicode_collation_protocol->metai_match(
|
||||
unicode_collation_protocol, c, L"Da[q-v] U-Boot");
|
||||
if (!ret) {
|
||||
efi_st_error("metai_match returned %u\n", ret);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
ret = unicode_collation_protocol->metai_match(
|
||||
unicode_collation_protocol, c, L"Da? U-Boot");
|
||||
if (!ret) {
|
||||
efi_st_error("metai_match returned %u\n", ret);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
ret = unicode_collation_protocol->metai_match(
|
||||
unicode_collation_protocol, c, L"D*Bo*t");
|
||||
if (!ret) {
|
||||
efi_st_error("metai_match returned %u\n", ret);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
ret = unicode_collation_protocol->metai_match(
|
||||
unicode_collation_protocol, c, L"Da[xyz] U-Boot");
|
||||
if (ret) {
|
||||
efi_st_error("metai_match returned %u\n", ret);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
ret = unicode_collation_protocol->metai_match(
|
||||
unicode_collation_protocol, c, L"Da[a-d] U-Boot");
|
||||
if (ret) {
|
||||
efi_st_error("metai_match returned %u\n", ret);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
ret = unicode_collation_protocol->metai_match(
|
||||
unicode_collation_protocol, c, L"Da?? U-Boot");
|
||||
if (ret) {
|
||||
efi_st_error("metai_match returned %u\n", ret);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
ret = unicode_collation_protocol->metai_match(
|
||||
unicode_collation_protocol, c, L"D*Bo*tt");
|
||||
if (ret) {
|
||||
efi_st_error("metai_match returned %u\n", ret);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
static int test_str_lwr(void)
|
||||
{
|
||||
u16 c[] = L"U-Boot";
|
||||
|
||||
unicode_collation_protocol->str_lwr(unicode_collation_protocol, c);
|
||||
if (efi_st_strcmp_16_8(c, "u-boot")) {
|
||||
efi_st_error("str_lwr returned \"%ps\"\n", c);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
static int test_str_upr(void)
|
||||
{
|
||||
u16 c[] = L"U-Boot";
|
||||
|
||||
unicode_collation_protocol->str_upr(unicode_collation_protocol, c);
|
||||
if (efi_st_strcmp_16_8(c, "U-BOOT")) {
|
||||
efi_st_error("str_lwr returned \"%ps\"\n", c);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
static int test_fat_to_str(void)
|
||||
{
|
||||
u16 str[16];
|
||||
|
||||
boottime->set_mem(str, sizeof(str), 0);
|
||||
unicode_collation_protocol->fat_to_str(unicode_collation_protocol, 6,
|
||||
"U-BOOT", str);
|
||||
if (efi_st_strcmp_16_8(str, "U-BOOT")) {
|
||||
efi_st_error("fat_to_str returned \"%ps\"\n", str);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
static int test_str_to_fat(void)
|
||||
{
|
||||
char fat[16];
|
||||
bool ret;
|
||||
|
||||
boottime->set_mem(fat, sizeof(fat), 0);
|
||||
ret = unicode_collation_protocol->str_to_fat(unicode_collation_protocol,
|
||||
L"U -Boo.t", 6, fat);
|
||||
if (ret || efi_st_strcmp_16_8(L"U-BOOT", fat)) {
|
||||
efi_st_error("str_to_fat returned %u, \"%s\"\n", ret, fat);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
boottime->set_mem(fat, 16, 0);
|
||||
ret = unicode_collation_protocol->str_to_fat(unicode_collation_protocol,
|
||||
L"U\\Boot", 6, fat);
|
||||
if (!ret || efi_st_strcmp_16_8(L"U_BOOT", fat)) {
|
||||
efi_st_error("str_to_fat returned %u, \"%s\"\n", ret, fat);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* execute() - Execute unit test.
|
||||
*
|
||||
* ReturnValue: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!unicode_collation_protocol) {
|
||||
efi_st_printf("Unicode collation protocol missing\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
ret = test_stri_coll();
|
||||
if (ret != EFI_ST_SUCCESS)
|
||||
return ret;
|
||||
|
||||
ret = test_metai_match();
|
||||
if (ret != EFI_ST_SUCCESS)
|
||||
return ret;
|
||||
|
||||
ret = test_str_lwr();
|
||||
if (ret != EFI_ST_SUCCESS)
|
||||
return ret;
|
||||
|
||||
ret = test_str_upr();
|
||||
if (ret != EFI_ST_SUCCESS)
|
||||
return ret;
|
||||
|
||||
ret = test_fat_to_str();
|
||||
if (ret != EFI_ST_SUCCESS)
|
||||
return ret;
|
||||
|
||||
ret = test_str_to_fat();
|
||||
if (ret != EFI_ST_SUCCESS)
|
||||
return ret;
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(unicoll) = {
|
||||
.name = "unicode collation",
|
||||
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
.execute = execute,
|
||||
.setup = setup,
|
||||
};
|
||||
@@ -0,0 +1,112 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_util
|
||||
*
|
||||
* Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* Utility functions
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
|
||||
struct efi_st_translate {
|
||||
u16 code;
|
||||
u16 *text;
|
||||
};
|
||||
|
||||
static struct efi_st_translate efi_st_control_characters[] = {
|
||||
{0, L"Null"},
|
||||
{8, L"BS"},
|
||||
{9, L"TAB"},
|
||||
{10, L"LF"},
|
||||
{13, L"CR"},
|
||||
{0, NULL},
|
||||
};
|
||||
|
||||
static u16 efi_st_ch[] = L"' '";
|
||||
static u16 efi_st_unknown[] = L"unknown";
|
||||
|
||||
static struct efi_st_translate efi_st_scan_codes[] = {
|
||||
{0x00, L"Null"},
|
||||
{0x01, L"Up"},
|
||||
{0x02, L"Down"},
|
||||
{0x03, L"Right"},
|
||||
{0x04, L"Left"},
|
||||
{0x05, L"Home"},
|
||||
{0x06, L"End"},
|
||||
{0x07, L"Insert"},
|
||||
{0x08, L"Delete"},
|
||||
{0x09, L"Page Up"},
|
||||
{0x0a, L"Page Down"},
|
||||
{0x0b, L"FN 1"},
|
||||
{0x0c, L"FN 2"},
|
||||
{0x0d, L"FN 3"},
|
||||
{0x0e, L"FN 4"},
|
||||
{0x0f, L"FN 5"},
|
||||
{0x10, L"FN 6"},
|
||||
{0x11, L"FN 7"},
|
||||
{0x12, L"FN 8"},
|
||||
{0x13, L"FN 9"},
|
||||
{0x14, L"FN 10"},
|
||||
{0x15, L"FN 11"},
|
||||
{0x16, L"FN 12"},
|
||||
{0x17, L"Escape"},
|
||||
{0x68, L"FN 13"},
|
||||
{0x69, L"FN 14"},
|
||||
{0x6a, L"FN 15"},
|
||||
{0x6b, L"FN 16"},
|
||||
{0x6c, L"FN 17"},
|
||||
{0x6d, L"FN 18"},
|
||||
{0x6e, L"FN 19"},
|
||||
{0x6f, L"FN 20"},
|
||||
{0x70, L"FN 21"},
|
||||
{0x71, L"FN 22"},
|
||||
{0x72, L"FN 23"},
|
||||
{0x73, L"FN 24"},
|
||||
{0x7f, L"Mute"},
|
||||
{0x80, L"Volume Up"},
|
||||
{0x81, L"Volume Down"},
|
||||
{0x100, L"Brightness Up"},
|
||||
{0x101, L"Brightness Down"},
|
||||
{0x102, L"Suspend"},
|
||||
{0x103, L"Hibernate"},
|
||||
{0x104, L"Toggle Display"},
|
||||
{0x105, L"Recovery"},
|
||||
{0x106, L"Reject"},
|
||||
{0x0, NULL},
|
||||
};
|
||||
|
||||
u16 *efi_st_translate_char(u16 code)
|
||||
{
|
||||
struct efi_st_translate *tr;
|
||||
|
||||
if (code >= ' ') {
|
||||
efi_st_ch[1] = code;
|
||||
return efi_st_ch;
|
||||
}
|
||||
for (tr = efi_st_control_characters; tr->text; ++tr) {
|
||||
if (tr->code == code)
|
||||
return tr->text;
|
||||
}
|
||||
return efi_st_unknown;
|
||||
}
|
||||
|
||||
u16 *efi_st_translate_code(u16 code)
|
||||
{
|
||||
struct efi_st_translate *tr;
|
||||
|
||||
for (tr = efi_st_scan_codes; tr->text; ++tr) {
|
||||
if (tr->code == code)
|
||||
return tr->text;
|
||||
}
|
||||
return efi_st_unknown;
|
||||
}
|
||||
|
||||
int efi_st_strcmp_16_8(const u16 *buf1, const char *buf2)
|
||||
{
|
||||
for (; *buf1 || *buf2; ++buf1, ++buf2) {
|
||||
if (*buf1 != *buf2)
|
||||
return *buf1 - *buf2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_variables
|
||||
*
|
||||
* Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* This unit test checks the runtime services for variables:
|
||||
* GetVariable, GetNextVariableName, SetVariable, QueryVariableInfo.
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
|
||||
#define EFI_ST_MAX_DATA_SIZE 16
|
||||
#define EFI_ST_MAX_VARNAME_SIZE 40
|
||||
|
||||
static struct efi_boot_services *boottime;
|
||||
static struct efi_runtime_services *runtime;
|
||||
static const efi_guid_t guid_vendor0 =
|
||||
EFI_GUID(0x67029eb5, 0x0af2, 0xf6b1,
|
||||
0xda, 0x53, 0xfc, 0xb5, 0x66, 0xdd, 0x1c, 0xe6);
|
||||
static const efi_guid_t guid_vendor1 =
|
||||
EFI_GUID(0xff629290, 0x1fc1, 0xd73f,
|
||||
0x8f, 0xb1, 0x32, 0xf9, 0x0c, 0xa0, 0x42, 0xea);
|
||||
|
||||
/*
|
||||
* Setup unit test.
|
||||
*
|
||||
* @handle handle of the loaded image
|
||||
* @systable system table
|
||||
*/
|
||||
static int setup(const efi_handle_t img_handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
boottime = systable->boottime;
|
||||
runtime = systable->runtime;
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute unit test.
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
efi_status_t ret;
|
||||
efi_uintn_t len;
|
||||
u32 attr;
|
||||
u8 v[16] = {0x5d, 0xd1, 0x5e, 0x51, 0x5a, 0x05, 0xc7, 0x0c,
|
||||
0x35, 0x4a, 0xae, 0x87, 0xa5, 0xdf, 0x0f, 0x65,};
|
||||
u8 data[EFI_ST_MAX_DATA_SIZE];
|
||||
u16 varname[EFI_ST_MAX_VARNAME_SIZE];
|
||||
int flag;
|
||||
efi_guid_t guid;
|
||||
u64 max_storage, rem_storage, max_size;
|
||||
|
||||
ret = runtime->query_variable_info(EFI_VARIABLE_BOOTSERVICE_ACCESS,
|
||||
&max_storage, &rem_storage,
|
||||
&max_size);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_todo("QueryVariableInfo failed\n");
|
||||
} else if (!max_storage || !rem_storage || !max_size) {
|
||||
efi_st_error("QueryVariableInfo: wrong info\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Set variable 0 */
|
||||
ret = runtime->set_variable(L"efi_st_var0", &guid_vendor0,
|
||||
EFI_VARIABLE_BOOTSERVICE_ACCESS,
|
||||
3, v + 4);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("SetVariable failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
data[3] = 0xff;
|
||||
len = 3;
|
||||
ret = runtime->get_variable(L"efi_st_var0", &guid_vendor0,
|
||||
&attr, &len, data);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("GetVariable failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (memcmp(data, v + 4, 3)) {
|
||||
efi_st_error("GetVariable returned wrong value\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (data[3] != 0xff) {
|
||||
efi_st_error("GetVariable wrote past the end of the buffer\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Set variable 1 */
|
||||
ret = runtime->set_variable(L"efi_st_var1", &guid_vendor1,
|
||||
EFI_VARIABLE_BOOTSERVICE_ACCESS,
|
||||
8, v);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("SetVariable failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
len = EFI_ST_MAX_DATA_SIZE;
|
||||
ret = runtime->get_variable(L"efi_st_var1", &guid_vendor1,
|
||||
&attr, &len, data);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("GetVariable failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (len != 8) {
|
||||
efi_st_error("GetVariable returned wrong length %u\n",
|
||||
(unsigned int)len);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (memcmp(data, v, 8)) {
|
||||
efi_st_error("GetVariable returned wrong value\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Append variable 1 */
|
||||
ret = runtime->set_variable(L"efi_st_var1", &guid_vendor1,
|
||||
EFI_VARIABLE_BOOTSERVICE_ACCESS |
|
||||
EFI_VARIABLE_APPEND_WRITE,
|
||||
7, v + 8);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("SetVariable(APPEND_WRITE) failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
len = EFI_ST_MAX_DATA_SIZE;
|
||||
ret = runtime->get_variable(L"efi_st_var1", &guid_vendor1,
|
||||
&attr, &len, data);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("GetVariable failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (len != 15)
|
||||
efi_st_todo("GetVariable returned wrong length %u\n",
|
||||
(unsigned int)len);
|
||||
if (memcmp(data, v, len))
|
||||
efi_st_todo("GetVariable returned wrong value\n");
|
||||
/* Append variable 2 */
|
||||
ret = runtime->set_variable(L"efi_none", &guid_vendor1,
|
||||
EFI_VARIABLE_BOOTSERVICE_ACCESS |
|
||||
EFI_VARIABLE_APPEND_WRITE,
|
||||
15, v);
|
||||
if (ret != EFI_NOT_FOUND) {
|
||||
efi_st_error("SetVariable(APPEND_WRITE) with size 0 to non-existent variable returns wrong code\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Enumerate variables */
|
||||
boottime->set_mem(&guid, 16, 0);
|
||||
*varname = 0;
|
||||
flag = 0;
|
||||
for (;;) {
|
||||
len = EFI_ST_MAX_VARNAME_SIZE;
|
||||
ret = runtime->get_next_variable_name(&len, varname, &guid);
|
||||
if (ret == EFI_NOT_FOUND)
|
||||
break;
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("GetNextVariableName failed (%u)\n",
|
||||
(unsigned int)ret);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (!memcmp(&guid, &guid_vendor0, sizeof(efi_guid_t)) &&
|
||||
!efi_st_strcmp_16_8(varname, "efi_st_var0"))
|
||||
flag |= 1;
|
||||
if (!memcmp(&guid, &guid_vendor1, sizeof(efi_guid_t)) &&
|
||||
!efi_st_strcmp_16_8(varname, "efi_st_var1"))
|
||||
flag |= 2;
|
||||
}
|
||||
if (flag != 3) {
|
||||
efi_st_error(
|
||||
"GetNextVariableName did not return all variables\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Delete variable 1 */
|
||||
ret = runtime->set_variable(L"efi_st_var1", &guid_vendor1,
|
||||
0, 0, NULL);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("SetVariable failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
len = EFI_ST_MAX_DATA_SIZE;
|
||||
ret = runtime->get_variable(L"efi_st_var1", &guid_vendor1,
|
||||
&attr, &len, data);
|
||||
if (ret != EFI_NOT_FOUND) {
|
||||
efi_st_error("Variable was not deleted\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
/* Delete variable 0 */
|
||||
ret = runtime->set_variable(L"efi_st_var0", &guid_vendor0,
|
||||
0, 0, NULL);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("SetVariable failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
len = EFI_ST_MAX_DATA_SIZE;
|
||||
ret = runtime->get_variable(L"efi_st_var0", &guid_vendor0,
|
||||
&attr, &len, data);
|
||||
if (ret != EFI_NOT_FOUND) {
|
||||
efi_st_error("Variable was not deleted\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(variables) = {
|
||||
.name = "variables",
|
||||
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup,
|
||||
.execute = execute,
|
||||
};
|
||||
@@ -0,0 +1,94 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_variables_runtime
|
||||
*
|
||||
* Copyright (c) 2019 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* This unit test checks the runtime services for variables after
|
||||
* ExitBootServices():
|
||||
* GetVariable, GetNextVariableName, SetVariable, QueryVariableInfo.
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
|
||||
#define EFI_ST_MAX_DATA_SIZE 16
|
||||
#define EFI_ST_MAX_VARNAME_SIZE 40
|
||||
|
||||
static struct efi_boot_services *boottime;
|
||||
static struct efi_runtime_services *runtime;
|
||||
static const efi_guid_t guid_vendor0 =
|
||||
EFI_GUID(0x67029eb5, 0x0af2, 0xf6b1,
|
||||
0xda, 0x53, 0xfc, 0xb5, 0x66, 0xdd, 0x1c, 0xe6);
|
||||
|
||||
/*
|
||||
* Setup unit test.
|
||||
*
|
||||
* @handle handle of the loaded image
|
||||
* @systable system table
|
||||
*/
|
||||
static int setup(const efi_handle_t img_handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
boottime = systable->boottime;
|
||||
runtime = systable->runtime;
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* execute() - execute unit test
|
||||
*
|
||||
* As runtime support is not implmented expect EFI_UNSUPPORTED to be returned.
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
efi_status_t ret;
|
||||
efi_uintn_t len;
|
||||
u32 attr;
|
||||
u8 v[16] = {0x5d, 0xd1, 0x5e, 0x51, 0x5a, 0x05, 0xc7, 0x0c,
|
||||
0x35, 0x4a, 0xae, 0x87, 0xa5, 0xdf, 0x0f, 0x65,};
|
||||
u8 data[EFI_ST_MAX_DATA_SIZE];
|
||||
u16 varname[EFI_ST_MAX_VARNAME_SIZE];
|
||||
efi_guid_t guid;
|
||||
u64 max_storage, rem_storage, max_size;
|
||||
|
||||
ret = runtime->query_variable_info(EFI_VARIABLE_BOOTSERVICE_ACCESS,
|
||||
&max_storage, &rem_storage,
|
||||
&max_size);
|
||||
if (ret != EFI_UNSUPPORTED) {
|
||||
efi_st_error("QueryVariableInfo failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
ret = runtime->set_variable(L"efi_st_var0", &guid_vendor0,
|
||||
EFI_VARIABLE_BOOTSERVICE_ACCESS |
|
||||
EFI_VARIABLE_RUNTIME_ACCESS,
|
||||
3, v + 4);
|
||||
if (ret != EFI_UNSUPPORTED) {
|
||||
efi_st_error("SetVariable failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
len = 3;
|
||||
ret = runtime->get_variable(L"efi_st_var0", &guid_vendor0,
|
||||
&attr, &len, data);
|
||||
if (ret != EFI_UNSUPPORTED) {
|
||||
efi_st_error("GetVariable failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
memset(&guid, 0, 16);
|
||||
*varname = 0;
|
||||
ret = runtime->get_next_variable_name(&len, varname, &guid);
|
||||
if (ret != EFI_UNSUPPORTED) {
|
||||
efi_st_error("GetNextVariableName failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(variables_run) = {
|
||||
.name = "variables at runtime",
|
||||
.phase = EFI_SETUP_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup,
|
||||
.execute = execute,
|
||||
};
|
||||
@@ -0,0 +1,230 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* efi_selftest_watchdog
|
||||
*
|
||||
* Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
|
||||
*
|
||||
* The 'watchdog timer' unit test checks that the watchdog timer
|
||||
* will not cause a system restart during the timeout period after
|
||||
* a timer reset.
|
||||
*
|
||||
* The 'watchdog reboot' unit test checks that the watchdog timer
|
||||
* actually reboots the system after a timeout. The test is only
|
||||
* executed on explicit request. Use the following commands:
|
||||
*
|
||||
* setenv efi_selftest watchdog reboot
|
||||
* bootefi selftest
|
||||
*/
|
||||
|
||||
#include <efi_selftest.h>
|
||||
|
||||
/*
|
||||
* This is the communication structure for the notification function.
|
||||
*/
|
||||
struct notify_context {
|
||||
/* Status code returned when resetting watchdog */
|
||||
efi_status_t status;
|
||||
/* Number of invocations of the notification function */
|
||||
unsigned int timer_ticks;
|
||||
};
|
||||
|
||||
static struct efi_event *event_notify;
|
||||
static struct efi_event *event_wait;
|
||||
static struct efi_boot_services *boottime;
|
||||
static struct notify_context notification_context;
|
||||
static bool watchdog_reset;
|
||||
|
||||
/*
|
||||
* Notification function, increments the notification count if parameter
|
||||
* context is provided.
|
||||
*
|
||||
* @event notified event
|
||||
* @context pointer to the timeout
|
||||
*/
|
||||
static void EFIAPI notify(struct efi_event *event, void *context)
|
||||
{
|
||||
struct notify_context *notify_context = context;
|
||||
efi_status_t ret = EFI_SUCCESS;
|
||||
|
||||
if (!notify_context)
|
||||
return;
|
||||
|
||||
/* Reset watchdog timer to one second */
|
||||
ret = boottime->set_watchdog_timer(1, 0, 0, NULL);
|
||||
if (ret != EFI_SUCCESS)
|
||||
notify_context->status = ret;
|
||||
/* Count number of calls */
|
||||
notify_context->timer_ticks++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup unit test.
|
||||
*
|
||||
* Create two timer events.
|
||||
* One with EVT_NOTIFY_SIGNAL, the other with EVT_NOTIFY_WAIT.
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int setup(const efi_handle_t handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
efi_status_t ret;
|
||||
|
||||
boottime = systable->boottime;
|
||||
|
||||
notification_context.status = EFI_SUCCESS;
|
||||
notification_context.timer_ticks = 0;
|
||||
ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL,
|
||||
TPL_CALLBACK, notify,
|
||||
(void *)¬ification_context,
|
||||
&event_notify);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("could not create event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_WAIT,
|
||||
TPL_CALLBACK, notify, NULL, &event_wait);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("could not create event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute the test resetting the watchdog in a timely manner. No reboot occurs.
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int setup_timer(const efi_handle_t handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
watchdog_reset = true;
|
||||
return setup(handle, systable);
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute the test without resetting the watchdog. A system reboot occurs.
|
||||
*
|
||||
* @handle: handle of the loaded image
|
||||
* @systable: system table
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int setup_reboot(const efi_handle_t handle,
|
||||
const struct efi_system_table *systable)
|
||||
{
|
||||
watchdog_reset = false;
|
||||
return setup(handle, systable);
|
||||
}
|
||||
|
||||
/*
|
||||
* Tear down unit test.
|
||||
*
|
||||
* Close the events created in setup.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int teardown(void)
|
||||
{
|
||||
efi_status_t ret;
|
||||
|
||||
/* Set the watchdog timer to the five minute default value */
|
||||
ret = boottime->set_watchdog_timer(300, 0, 0, NULL);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Setting watchdog timer failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (event_notify) {
|
||||
ret = boottime->close_event(event_notify);
|
||||
event_notify = NULL;
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Could not close event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
}
|
||||
if (event_wait) {
|
||||
ret = boottime->close_event(event_wait);
|
||||
event_wait = NULL;
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Could not close event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
}
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute unit test.
|
||||
*
|
||||
* Run a 600 ms periodic timer that resets the watchdog to one second
|
||||
* on every timer tick.
|
||||
*
|
||||
* Run a 1350 ms single shot timer and check that the 600ms timer has
|
||||
* been called 2 times.
|
||||
*
|
||||
* @return: EFI_ST_SUCCESS for success
|
||||
*/
|
||||
static int execute(void)
|
||||
{
|
||||
size_t index;
|
||||
efi_status_t ret;
|
||||
|
||||
/* Set the watchdog timeout to one second */
|
||||
ret = boottime->set_watchdog_timer(1, 0, 0, NULL);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Setting watchdog timer failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (watchdog_reset) {
|
||||
/* Set 600 ms timer */
|
||||
ret = boottime->set_timer(event_notify, EFI_TIMER_PERIODIC,
|
||||
6000000);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Could not set timer\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
}
|
||||
/* Set 1350 ms timer */
|
||||
ret = boottime->set_timer(event_wait, EFI_TIMER_RELATIVE, 13500000);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Could not set timer\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
|
||||
ret = boottime->wait_for_event(1, &event_wait, &index);
|
||||
if (ret != EFI_SUCCESS) {
|
||||
efi_st_error("Could not wait for event\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (notification_context.status != EFI_SUCCESS) {
|
||||
efi_st_error("Setting watchdog timer failed\n");
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
if (notification_context.timer_ticks != 2) {
|
||||
efi_st_error("The timer was called %u times, expected 2.\n",
|
||||
notification_context.timer_ticks);
|
||||
return EFI_ST_FAILURE;
|
||||
}
|
||||
return EFI_ST_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_UNIT_TEST(watchdog1) = {
|
||||
.name = "watchdog timer",
|
||||
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup_timer,
|
||||
.execute = execute,
|
||||
.teardown = teardown,
|
||||
};
|
||||
|
||||
EFI_UNIT_TEST(watchdog2) = {
|
||||
.name = "watchdog reboot",
|
||||
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
|
||||
.setup = setup_reboot,
|
||||
.execute = execute,
|
||||
.teardown = teardown,
|
||||
.on_request = true,
|
||||
};
|
||||
Reference in New Issue
Block a user