1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * efi_selftest_set_virtual_address_map.c 4 * 5 * Copyright (c) 2019 Heinrich Schuchardt <xypron.glpk@gmx.de> 6 * 7 * This test checks the notification of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE 8 * and the following services: SetVirtualAddressMap, ConvertPointer. 9 */ 10 11#include <efi_selftest.h> 12 13static const struct efi_boot_services *boottime; 14static const struct efi_runtime_services *runtime; 15static struct efi_event *event; 16static struct efi_mem_desc *memory_map; 17static efi_uintn_t map_size; 18static efi_uintn_t desc_size; 19static u32 desc_version; 20static u64 page1; 21static u64 page2; 22static u32 notify_call_count; 23static bool convert_pointer_failed; 24 25/** 26 * notify() - notification function 27 * 28 * This function is called when the EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event 29 * occurs. The correct output of ConvertPointer() is checked. 30 * 31 * @event notified event 32 * @context pointer to the notification count 33 */ 34static void EFIAPI notify(struct efi_event *event, void *context) 35{ 36 void *addr; 37 efi_status_t ret; 38 39 ++notify_call_count; 40 41 addr = (void *)(uintptr_t)page1; 42 ret = runtime->convert_pointer(0, &addr); 43 if (ret != EFI_SUCCESS) { 44 efi_st_error("ConvertPointer failed\n"); 45 convert_pointer_failed = true; 46 return; 47 } 48 if ((uintptr_t)addr != page1 + EFI_PAGE_SIZE) { 49 efi_st_error("ConvertPointer wrong address\n"); 50 convert_pointer_failed = true; 51 return; 52 } 53 54 addr = (void *)(uintptr_t)page2; 55 ret = runtime->convert_pointer(0, &addr); 56 if (ret != EFI_SUCCESS) { 57 efi_st_error("ConvertPointer failed\n"); 58 convert_pointer_failed = true; 59 return; 60 } 61 if ((uintptr_t)addr != page2 + 2 * EFI_PAGE_SIZE) { 62 efi_st_error("ConvertPointer wrong address\n"); 63 convert_pointer_failed = true; 64 } 65} 66 67/** 68 * setup() - setup unit test 69 * 70 * The memory map is read. Boottime only entries are deleted. Two entries for 71 * newly allocated pages are added. For these virtual addresses deviating from 72 * the physical addresses are set. 73 * 74 * @handle: handle of the loaded image 75 * @systable: system table 76 * Return: EFI_ST_SUCCESS for success 77 */ 78static int setup(const efi_handle_t handle, 79 const struct efi_system_table *systable) 80{ 81 efi_uintn_t map_key; 82 efi_status_t ret; 83 struct efi_mem_desc *end, *pos1, *pos2; 84 85 boottime = systable->boottime; 86 runtime = systable->runtime; 87 88 ret = boottime->create_event(EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE, 89 TPL_CALLBACK, notify, NULL, 90 &event); 91 if (ret != EFI_SUCCESS) { 92 efi_st_error("could not create event\n"); 93 return EFI_ST_FAILURE; 94 } 95 96 ret = boottime->get_memory_map(&map_size, NULL, &map_key, &desc_size, 97 &desc_version); 98 if (ret != EFI_BUFFER_TOO_SMALL) { 99 efi_st_error( 100 "GetMemoryMap did not return EFI_BUFFER_TOO_SMALL\n"); 101 return EFI_ST_FAILURE; 102 } 103 /* Allocate extra space for newly allocated memory */ 104 map_size += 3 * sizeof(struct efi_mem_desc); 105 ret = boottime->allocate_pool(EFI_BOOT_SERVICES_DATA, map_size, 106 (void **)&memory_map); 107 if (ret != EFI_SUCCESS) { 108 efi_st_error("AllocatePool failed\n"); 109 return EFI_ST_FAILURE; 110 } 111 ret = boottime->get_memory_map(&map_size, memory_map, &map_key, 112 &desc_size, &desc_version); 113 if (ret != EFI_SUCCESS) { 114 efi_st_error("GetMemoryMap failed\n"); 115 return EFI_ST_FAILURE; 116 } 117 ret = boottime->allocate_pages(EFI_ALLOCATE_ANY_PAGES, 118 EFI_BOOT_SERVICES_DATA, 2, &page1); 119 if (ret != EFI_SUCCESS) { 120 efi_st_error("AllocatePages failed\n"); 121 return EFI_ST_FAILURE; 122 } 123 ret = boottime->allocate_pages(EFI_ALLOCATE_ANY_PAGES, 124 EFI_BOOT_SERVICES_DATA, 3, &page2); 125 if (ret != EFI_SUCCESS) { 126 efi_st_error("AllocatePages failed\n"); 127 return EFI_ST_FAILURE; 128 } 129 /* Remove entries not relevant for runtime from map */ 130 end = (struct efi_mem_desc *)((u8 *)memory_map + map_size); 131 for (pos1 = memory_map, pos2 = memory_map; 132 pos2 < end; ++pos2) { 133 switch (pos2->type) { 134 case EFI_LOADER_CODE: 135 case EFI_LOADER_DATA: 136 case EFI_BOOT_SERVICES_CODE: 137 case EFI_BOOT_SERVICES_DATA: 138 case EFI_CONVENTIONAL_MEMORY: 139 continue; 140 } 141 memcpy(pos1, pos2, desc_size); 142 ++pos1; 143 } 144 145 /* 146 * Add entries with virtual addresses deviating from the physical 147 * addresses. By choosing virtual address ranges within the allocated 148 * physical pages address space collisions are avoided. 149 */ 150 pos1->type = EFI_RUNTIME_SERVICES_DATA; 151 pos1->reserved = 0; 152 pos1->physical_start = page1; 153 pos1->virtual_start = page1 + EFI_PAGE_SIZE; 154 pos1->num_pages = 1; 155 pos1->attribute = EFI_MEMORY_RUNTIME; 156 ++pos1; 157 158 pos1->type = EFI_RUNTIME_SERVICES_DATA; 159 pos1->reserved = 0; 160 pos1->physical_start = page2; 161 pos1->virtual_start = page2 + 2 * EFI_PAGE_SIZE; 162 pos1->num_pages = 1; 163 pos1->attribute = EFI_MEMORY_RUNTIME; 164 ++pos1; 165 166 map_size = (u8 *)pos1 - (u8 *)memory_map; 167 168 return EFI_ST_SUCCESS; 169} 170 171/** 172 * execute() - execute unit test 173 * 174 * SetVirtualAddressMap() is called with the memory map prepared in setup(). 175 * 176 * The triggering of the EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event is checked via 177 * the call count of the notification function. 178 * 179 * Return: EFI_ST_SUCCESS for success 180 */ 181static int execute(void) 182{ 183 efi_status_t ret; 184 185 ret = runtime->set_virtual_address_map(map_size, desc_size, 186 desc_version, memory_map); 187 if (ret != EFI_SUCCESS) { 188 efi_st_error("SetVirtualAddressMap failed\n"); 189 return EFI_ST_FAILURE; 190 } 191 if (notify_call_count != 1) { 192 efi_st_error("EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE triggered %d times\n", 193 notify_call_count); 194 return EFI_ST_FAILURE; 195 } 196 if (convert_pointer_failed) 197 return EFI_ST_FAILURE; 198 199 return EFI_ST_SUCCESS; 200} 201 202EFI_UNIT_TEST(virtaddrmap) = { 203 .name = "virtual address map", 204 .phase = EFI_SETTING_VIRTUAL_ADDRESS_MAP, 205 .setup = setup, 206 .execute = execute, 207}; 208