blob: a4e5a50f632bd9b262adb5193ce20d1503489ee8 [file] [log] [blame]
// 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,
};