GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)

This commit is contained in:
lai
2026-09-06 03:52:57 +08:00
commit b1928b41c0
21813 changed files with 4413081 additions and 0 deletions
@@ -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 *)&notification_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,
};
@@ -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 *)&notification_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 *)&notification_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,
};