| // 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 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 = st_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 = st_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; | |
| ret = st_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 = st_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 = st_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 = st_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 = st_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 = st_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 = st_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_SETTING_VIRTUAL_ADDRESS_MAP, | |
| .setup = setup, | |
| .execute = execute, | |
| }; |